Java: передача параметров по значению или по ссылке?
Термины “передача по значению” и “передача по ссылке” имеют особые, точные определения в программировании. Эти термины отличаются от интуиции, которая возникает у многих людей, когда они впервые слышат данные определения.
В терминах “передача по значению” и “передача по ссылке” говорится о переменных. Передача по значению указывает, что функции или методу передается значение переменной. Передача по ссылке обозначает, что функции или методу передается ссылка на переменную (или косвенный адрес переменной в памяти), это дает возможность изменять содержимое переменной.
Если исходить из этих определений, то в Java переменные всегда передаются по значению. К сожалению, когда мы имеем дело с переменными, содержащими объекты, мы на самом деле имеем дело с дескрипторами объектов, называемыми ссылками, которые также передаются по значению. Эта терминология и семантика легко сбивают с толку многих новичков.
public static void main(String[] args) < Cat aCat = new Cat("Max"); Cat oldCat = aCat; // передаем объект в функцию foo foo(aCat); // переменная aCat все еще будет указывать на кота "Max", после вызова foo() aCat.getName().equals("Max"); // true aCat.getName().equals("Nick"); // false aCat == oldCat; // true >public static void foo(Cat d) < d.getName().equals("Max"); // true // меняем d внутри foo(), d будет ссылаться на новый экземпляр Cat с именем "Nick" d = new Cat("Nick"); d.getName().equals("Nick"); // true >
В приведенном выше примере aCat.getName() все равно вернет строку “Max”. Значение aCat внутри main не изменяется в функции foo, поскольку ссылка на объект передается по значению. Если сам объект был бы передан по ссылке, то вызов метода aCat.getName() в main вернул бы строку “Nick” после вызова foo.
В C++, C, Pascal и других языках, поддерживающих прямую передачу по ссылке, вы действительно можете изменить переданную переменную, в отличии от Java.
В Java аргументы примитивных типов (int, long и т.д.) по умолчанию передаются по значению, а объекты по умолчанию передаются по значению ссылки на объект.
Как передаются параметры в java
[an error occurred while processing this directive]
Гадание на кофейной гуще, или передача по значению в Java
[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive] (none)
[an error occurred while processing this directive] ([an error occurred while processing this directive] к.ф.м.н. Андрей Озеров [an error occurred while processing this directive])
[an error occurred while processing this directive](none)
(Примечание редактора колонки Андрея Терешко, далее — прим. редактора: Не стоит гадать на кофейной гуще, надо больше пить кофе — этот прелестный напиток. 😉 )
Однажды, придя в очередной раз на техническое интервью для устройства на работу в одну IT — компанию, с целью проверки моих скромных познаний основ языка Java, меня спросили: “Как происходит передача объектов в методы в качестве параметров?”. Данный вопрос не застал меня врасплох, и я с гордостью ответил: “Передача примитивов в методы в качестве параметров происходит по значению, а объектов – по ссылке”. Данный ответ вполне удовлетворил моего будущего коллегу, и после непродолжительной беседы я был принят в компанию в качестве разработчика программного обеспечения. (прим. редактора: если бы спрашивающий более досконально разбирался в программировании на Java, то автора статьи не приняли бы на работу и … пропал бы программист для Java. 😉 ).
Однако спустя некоторое время я все больше и больше стал задумываться над проблемой передачи и возвращении объектов в/из методов. На мои размышления огромное влияние оказала книга Брюса Эккеля, где он подробно описывает поведение объекта при передаче ссылки на него в качестве параметра методу. Да, я не оговорился, действительно передача объекта в метод происходит путем передачи ссылки на этот объект. (прим. редактора: точнее — передача методу в качестве параметра переменной некоторого типа. Тип может быть «примитивным» — это boolean, char, int и т.д. и «объектным» — это то, что не относится к примитивному типу. В случае объектного типа методу в качестве параметра действительно передается ссылка на объект — не передавать же весь объект в метод? Но, что же это за ссылка передается?). Тогда возьмем для примера простую программку и посмотрим, измениться ли объект, если в методе переопределить эту ссылку. (прим. редактора: более точно, данный пример проверяет, окажет ли какое либо влияние в вызывающем методе изменение, совершенное в вызванном методе и заключающееся в изменении значения ссылки на параметр объектного типа, переданный методу).
public class HelloWorld < static void changeIt(String value) < value = new String("Hello!"); >public static void main(String[] argvc) < String test = new String("Hello World!"); changeIt(test); System.out.println("After changing : " + test); >>
Результат выполнения программы из Листинга 1 немного удивил меня. Объект test в методе main не изменил своего состояния после вызова статического метода класса — changeIt. (прим. редактора: если бы это произошло, то это было бы печально.) Следовательно, в данном случае здесь происходит какое-то дополнительное действие, которое я упустил.
Самое время обратиться к первоисточникам, вот что по этому поводу сказано в спецификации самого языка: “Method parameters (§8.4.1) name argument values passed to a method. For every parameter declared in a method declaration, a new parameter variable is created each time that method is invoked (§15.12). The new variable is initialized with the corresponding argument value from the method invocation. The method parameter effectively ceases to exist when the execution of the body of the method is complete.” Иными словами для каждого параметра метода создается своя локальная копия. Следовательно, в Java методах все аргументы передаются по значению. В случае, когда аргумент – примитивный тип, передача по значению означает то, что метод не может изменить оригинальное значение. Когда же аргумент – ссылка на объект, создается локальная копия ссылки, которая указывает на тот же самый объект, при изменении которой оригинальное состояние объекта остается неизменным. (прим. редактора: верно — ВСЕ ПАРАМЕТРЫ В JAVA ПЕРЕДАЮТСЯ ПО ЗНАЧЕНИЮ. Если параметр — ссылка на объект, то ЗНАЧЕНИЕМ является ЗНАЧЕНИЕ самой ссылки, а не значение разнообразных полей в объекте, коих может быть великое множество, как по количеству, так и по разнообразию типов).
Теперь все становиться на свои места. В случае программы из Листинга 1. мы передали ссылку на объект типа String в метод, где создалась временная копия этой ссылки. (прим. редактора: точнее, сначала была создана копия ссылки (временная или нет — это отдельный разговор. Возможно, эта копия «переживет» и свой оригинал, ведь вызванный метод объекта может сохранить эту копию в поле своего или чужого объекта), а затем эта копия ссылки и была передана вызванному методу. То есть, в памяти уже содержатся две ссылки, содержащие одно и тоже значение! И если бы в вызванном методе был вызван еще другой метод с передачей ему параметра в виде ссылки на тот же объект, то в памяти находилось бы уже три ссылки, содержащих одно и тоже значение! И т.д.) Затем мы переопределили ссылку внутри метода, теперь она указывает на другой объект, но по возвращении из метода мы продолжаем работать с оригинальной ссылкой test, которая осталась неизменной.
Теперь давайте рассмотрим другой пример.
public class HelloWorld < private int iValue; static void changeIt(HelloWorld value) < value.iValue = 10; >public static void main(String[] argvc) < HelloWorld hw = new HelloWorld(); hw.iValue = 20; changeIt(hw); System.out.println("After changing : " + hw.iValue); >>
В данном случае мы создаем новый объект hw типа HelloWorld, присваиваем целому полю значение 20, и затем передаем ссылку на этот объект в статический метод класса, где пытаемся изменить состояние объекта. В результате после возвращения в метод main мы видим, что состояние оригинального объекта было изменено. Здесь мы передаем ссылку на оригинальный объект в метод, где через сам объект пытаемся изменить его состояние. В этом случае первоначальное состояние объекта, несомненно, будет изменено. (прим. редактора: точнее, передаем по значению(!) ссылку на объект, и, далее, используя эту переданную ссылку можно в вызванном методе изменять значение полей объекта, на который указывает переданная ссылка).
Подводя итог всему вышесказанному, хочется лишь отметить, что однозначного мнения нет, что имели в виду авторы и разработчики языка Java, когда определяли возможности передачи объектов в качестве параметров методов. (прим. редактора: авторы языка Java ОДНОЗНАЧНО имели в виду то, что ВСЕ ПАРАМЕТРЫ В JAVA ПЕРЕДАЮТСЯ ПО ЗНАЧЕНИЮ.) Однако на самом деле, вряд ли стоит уделять этому столь значительное внимание. Важно лишь помнить, что если Вы передаете ссылку на Ваш объект в метод, Вы должны быть готовы, что внутри этого метода состояние объекта может измениться. (прим. редактора: под «состоянием объекта» тут надо понимать — «значения полей объекта», которые действительно могут измениться. Но есть «непробиваемые» объекты — это объекты типа String и объекты типов классов-оболочек: Boolean, Character, Integer и т.п. Значение полей в объектах этих типов вообще нельзя изменить, даже владея ссылкой на объекты этого типа. И таких «непробиваемых» объектов в Java полно и, конечно, сам программист может создавать свои типы «непробиваемых» объектов, например, устанавливая модификатор доступа private для поля объекта и не предоставляя метод для его модификации.)
(Заключительное примечание редактора: Отсутствие «однозначного мнения», особенно у начинающих программировать на Java или, что более характерно, перешедших к программированию на Java с других языков вызвано, по моему мнению, тем, что в других языках программирования ссылка может содержать значение(!), указывающее на другую(!) ссылку. В Java ОБЪЕКТНАЯ ССЫЛКА ВСЕГДА СОДЕРЖИТ ЗНАЧЕНИЕ, УКАЗЫВАЮЩЕЕ НА ОБЪЕКТ или значение null. В Java ссылка не может содержать значение, указывающее на другую ссылку!
Но это не означает, что в Java нельзя строить связанные списки или древовидные структуры. В Java объектная ссылка может содержать значение, указывающее на объект, поле которого содержит объектную ссылку, содержащую значение, указывающее на другой объект… и т.д.
Если же программисту необходимо(!) сделать так, чтобы вызванный метод изменил значение ссылки, то надо «обернуть» эту ссылку специальным объектом (или использовать массив) и передать эту «обертку» в качестве параметра методу, который и изменит значение «завернутой в обертку» ссылки.
Можно запрограммировать и так, чтобы для изменения значения ссылки использовать возвращаемое значение метода:
String value = new String("Hello!"); value = modifyValue(value);
Если программисту требуется, чтобы вызванный метод изменил переданные ему значения переменных «примитивных» типов, то эти переменные можно также «обернуть» специальным объектом (или использовать массив) и передать эту «обертку» в качестве параметра.)
(P.S. редактора колонки:
Представьте себе, что Вы, уважаемый читатель, совершаете путешествие. Во время своего изумительного путешествия Вы встречаете многих людей. Некоторым из встречных Вы КОПИРУЕТЕ часть своей дорожной карты, обращаясь к ним с просьбой показать Ваш дальнейший путь. Вам в ответ те, кого Вы спрашиваете, возвращают частичные КОПИИ своих дорожных карт, руководствуясь которыми Вы и продолжаете свое изумительное путешествие. Что было бы, если бы Вы вынуждены были для продолжения своего путешествия отдавать оригинал(!) своей дорожной карты в руки встречного? Вашу дорожную карту могли бы разорвать! И Ваше изумительное путешествие могло бы тогда закончиться весьма печально.
Конечно, если какой-нибудь встреченный Вами плохой человек захочет сжечь город, узнав от Вас местоположение(!) этого незащищенного города, по предоставленной ему частичной копии Вашей дорожной карты, то он это вполне может сделать. Но, если на частичной копии Вашей дорожной карты, предоставленной этому плохому человеку, нанесены только защищенные(!) крепости, то у Вас есть очень большая уверенность, что к моменту прихода в эту крепость Вы не найдете там одни развалины, а Вас и согреют и накормят, как Вы и рассчитывали.
И, руководствуясь заветами товарища Штирлица (запоминается последняя фраза), еще раз повторю — ВСЕ ПАРАМЕТРЫ В JAVA ПЕРЕДАЮТСЯ ПО ЗНАЧЕНИЮ.)
Литература
- James Gosling, Bill Jo,Guy Steel, Gilad Bracha The Java TM Language Specification, Second Edition
- http://java.sun.com/docs/books/ jls/second_edition/html/typesValues.doc.html#28536
- Брюс Эккель, “Философия Java”, серия “Библиотека программиста”, издательство “Питер”, Санкт-Петербург, 2001 г.
[an error occurred while processing this directive]
[an error occurred while processing this directive] (none)
- Записки Робинзона с острова Java
- От редактора
- Добро пожаловать
- Ныряем. Обзор книг по Java 1996-1998 годов
- Дополнение к обзору книг
- Мечта программиста
- Умри, замри, воскресни…
- Волшебство
- Развесим ярлыки или — Три слоя, слой Второй
- Ловкость рук и никакого…
- Раскроем карты
- Гадание на кофейной гуще, или передача по значению в Java
- 10 причин, по которым нам нужен Java 3
< Вернуться на caйт:: Copyright © 1999 — 2010, IT • archiv. Как передаются параметры в java
В Java параметры передаются в методы и конструкторы классов. При вызове метода или конструктора, значения параметров указываются в скобках после имени метода или конструктора.
Например, метод с двумя параметрами может быть объявлен следующим образом:
public void myMethod(int param1, String param2) // Код метода >При вызове этого метода нужно передать два соответствующих значения параметров:
myMethod(42, "Hello");Здесь 42 будет передано в параметр param1 , а строка «Hello» в параметр param2
ru_java
Один из стандартных упреков в адрес Java со стороны новичков — отсутствие параметров, передаваемых по ссылке (как «&arg» в C++ или «var arg» в Паскале). На самом деле упрек столь же несостоятелен, как и отсутствие goto — по крайней мере, в 99.99% случаев. Действительно, грамотно спроектированный объектно-ориентированный код попросту не нуждается в таких параметрах (что подтверждается огромным количеством довольно качественных библиотек от Sun, которые прекрасно обходятся без передачи по ссылке). Хороший метод либо модифицирует состояние объекта (может быть, своего аргумента), либо возвращает всю информацию в новом объекте.
Все это так, но меня не оставляет мысль, что существует те самые 0.01% случаев, когда передача параметра по ссылке все же оправдана. (Ведь и goto очень-очень редко все-таки применялся в высокопрофессиональных библиотеках на C++ и Паскале.) В данный момент я, как мне кажется, столкнулся как раз с таким случаем, и мне интересно, какой вариант решения посоветуют уважаемые коллеги с этого форума. Задаю вопрос отчасти потому, что с моим коллегой по работе наши мнения разошлись радикально 🙂
Итак. У меня есть метод
public static DoubleRange rangeOf(PArray array)
Здесь PArray — некий класс, описывающий массив чисел, а DoubleRange — пара чисел min и max (аналог апачевского DoubleRange). Метод одновременно вычисляет минимум и максимум в массиве, которые и возвращает в виде экземпляра DoubleRange.Я хочу иметь перегруженную версию этого метода, которая, кроме диапазона min..max, вернет также индексы в массиве найденных минимума и максимума. Просто на всякий случай: подобная информация нужна редко. Это еще 2 целых числа, в моем случае long (мои массивы имеют 64-битовую индексацию). В Паскале можно было бы указать их в качестве дополнительных var-параметров. Что делать в Java?
Варианты следующие.
1) Создаем новый класс DoubleRangeAndIndexes, содержащий min, max, indexOfMin, indexOfMax, и возвращаем его экземпляр. Крайне уродливо: такой класс, в отличие от DoubleRange, не описывает никакой осмысленной сущности. Получается «класс ради метода», хотя на самом деле все должно быть наоборот — методы ради обслуживания классов.
2) Передаем оставшиеся 2 числа каким-нибудь кривым, но поддерживаемым в языке способом. Самый очевидный — массив:
public static DoubleRange rangeOf(PArray array, long[] minAndMaxIndexes)
Метод записывает индексы минимума и максимума в ячейки minAndMaxIndexes[0] и minAndMaxIndexes[1]. До чего же мерзко, однако. Особенно последующее использование: в коде появляются «магические номера» 0 и 1.3) Используем org.omg.CORBA.LongHolder:
public static DoubleRange rangeOf(PArray array, LongHolder indexOfMin, LongHolder indexOfMax)
Собственно, это единственный способ передать примитивное значение по ссылке в Java при помощи стандартного API. Более того, само происхождение пакета намекает на передачу параметров по ссылке — очевидно, эти Holder-ы появились в CORBA для взаимодействия с языками, поддерживающими такую возможность. В данный момент у меня так и сделано. Мой коллега, однако, грязно ругается: какое, мол, отношение имеет CORBA к моей алгоритмической библиотеке, тем более к подсчету минимумов и максимумов. А я говорю: зато этот пакет включен в стандартную поставку JRE.4) Пишем и используем свой эквивалент LongHolder, например, в виде вложенного класса, специально для этого метода. Не так плохо, как вариант #1, но все равно «класс ради метода». К тому же у пользователей возникает очевидный вопрос: зачем написан класс, стопроцентно эквивалентный уже имеющемуся в стандартных библиотеках?
5) Пишем свой класс MinAndMaxPosition, позволяющий хранить (и модифицировать) 2 целых числа с названиями min и max. Передаем вместо пары LongHolder-ов. Но разве такой класс — осмысленная сущность?
Хотелось бы послушать, какой вариант предпочли бы уважаемые коллеги. Или, может быть, существует совершенно иное решение?
На всякий случай, если кому-то интересно, вот обсуждение этого же вопроса на другом форуме: http://xpoint.ru/forums/thread/40881.xhtml
LJ Video
26 comments — :
( 26 comments — Leave a comment )Зачем вообще все эти классы в жаве. И куда указатели подевали нафиг ?
А если серьёзно то способ 1:
Создать наследника класса DoubleRange(если он не final) с нужными методам. И это будет правильно с точки зрения ООП.
С точки зрения ООП правильно не наследник, а новый класс, содержащий DoubleRange внутри. Но это «правильно» лишь формально, если забыть, что полученный класс ровно ни для чего не нужен — кроме обслуживания данного метода. И не имеет почти никакого семантического смысла. Вы можете указать примеры подобных классов в нормальных библиотеках?
Правильно и так и этак.
И вообще есть такой паттерн — ValueObject. Который не несёт в себе никакого семантического смысла кроме как передачи набора значений.
В чём проблема — не понимаю. По моему передавать в метод кучу параметров который он меняет куда как более некрасиво.
+1 к ValueObject 🙂
У нас в utils есть свой holder.class Holder < T getValue() < return value_; >void setValue(T value) < value_=value; >private T value_; >
(А также Pair, UnaryFunction, BinaryFunction, ImmutableMappedCollection)
(Да, естественно класс не точно такой, там еще конструкторы и еще что-то)
И зачем свой Holder, когда уже есть стандартные org.omg.CORBA.XxxHolder?а если Вам потребуется возвращать массив int’ов? LongSeqHolder там есть, а вот IntSeqHolder’а — нет
Придется искать _еще одну_ библиотеку стандартных классов;)(Deleted comment)
На самом деле даже пары Range не надо: если уж метод вернул индексы, то совершенно нет необходимости возвращать значения минимума и максимума. Ведь значения минимума и максимума, зная индексы, можно получить тривиально — в том редчайшем случае, когда требуются и индексы, и значения. Поэтому я и привел пример, возвращающий пару min/max: метод, возвращающий только минимум, может спокойно вернуть его индекс и этим ограничиться.
Здесь другая проблема: пара целочискленных индексов indexOfMin + indexOfMax не является LongRange! Range на то и range, что в нем min всегда меньше либо равен max (чего, конечно, нельзя сказать про indexOfMin и indexOfMax). В этом семантическая сущность Range, инвариант и нетривиальные способы применения (вроде получения пересечения диапазонов).
А заводить специальный класс для представления произвольной пары чисел мне кажется еще худшим решением, чем применение LongHolder. А почему пара, а не тройка? Почему не масссив? И как должны называться элементы этой пары: getFirstElement() и getSecondElement()? Мы приходим ко всем минусам способа 2, вдобавок сохраняя минус «класс, не описывающий осмысленной сущности».
Скоро придут closures и всех нас спасут.
И это намного больше, чем 0.01% случаев. Кроме мест, в которых кроме closures просто нет другого красивого решения, есть еще масса случаев, в которых решение вроде ниче, но с closure было бы намного элегантней.Точно придут? Я как-то не вижу, чтобы Нил, Джош и Боб договорились до чего-то.
Ну, время еще есть. Да и саму необходимость осознали уже все. Теперь решают только какой вариант лучше.
Поясните, пожалуйста: а как здесь помогают замыкания?
Ой. «замыкания».
Как страшно жить!Closures имеют доступ к локальным переменным, так что если передавать в этот метод сlosure, то return value не нужен вообще. Ни много, ни даже один.
Вместо класса с результатом пишем класс, который содержит всю эту фигню:
конструктор берёт массив. Отдельый метод вычисляет что нужно. И есть геттеры для выборки максиума, индекса максимума, и т.д. Тут сразу возникнет мысль назвать такой класс SmartDoubleArray. После чего все употребления слова Double закидываем в параметр дженерика (Type extends Comparable), и получаем уютную конструкцию. Имхо.
+1
Я обычно примерно такими решениями и пользуюсь. Особенно учитывая, что потом такой класс можно (при необходимости!) настроить на Visitor или Command pattern (хотя я вообще официальные названия паттернов не очень люблю и стараюсь написать интуитивные названия методов, подходящие к текущей задаче). Но closures было бы еще красивее.Кстати, та фигня с синтаксическим подсвечиванием комментариев и строк оказалась и правда прикольной 🙂 «В лоб» ее конечно можно решить, antlr и все дела, но хочется какой-то красоты.
Это решение мне кажется самым правильным — помимо LongHolder, насчет которого я не уверен. Извиняюсь, что не указал такой вариант сразу — он тоже рассматривался.
Тут минусы примерно те же самые: лишние сущности. Мой rangeOf — член довольно обширного класса net.algart.arrays.Arrays, в чем-то аналогичного java.util.Collections и содержащего множество функций общего характера для работы с моим классом PArray (точнее, его более общим предком net.algart.arrays.Array). Пакет net.algart.arrays весьма велик и сложен, и rangeOf — лишь одна очень мелкая функция среди тех, которые обрабатывают мои массивы. Да, создавать класс для решения задачи — идея обычно правильная, но в данном случае хочется обойтись самыми простыми, минимальными средствами. Ведь для 99% пользователей вполне достаточно очевидного метода rangeOf(PArray) с простой и краткой документацией. В варианте с LongHolder мы практически не усложняем пакет: просто добавляется версия метода с дополнительными возможностями, расположенная во вполне очевидном месте (рядом с куда более популярными rangeOf с одним параметром). А новый класс, даже самый простой, потребует куда более объемной документации и создаст кучу совершенно ненужных (в данном случае) вопросов. Скажем, какой у него hashCode? Неизменяем ли он? Безопасен ли в многопоточной среде? Каковы его инварианты? И т.д. Посмотрите документацию на (более простой!) DoubleRange. И все это бедному пользователю придется изучать только ради того, чтобы узнать пару индексов.
И усложение API — лишь один недостаток. Куда хуже, что полученный класс практически не имеет применений, кроме решения единственной и заведомо редкой задачи — получения пары индексов. По моим понятиям, всякий простой универсальный класс должен описывать полезную сущность и, соответственно, применяться множеством способов. Скажем, «точка», «список», «матрица» и т.д. — все такие классы, как и LongHolder и DoubleRange, находят множество применений. (Скажем, DoubleRange мне понадобился в той же библиотеке для совсем другой цели: описания «прямоугольной функции».) А кому нужен класс «минимум + максимум + их индексы»? Какой же это «smart»-массив?
Есть ведь и другая классическая ситуация, которая на первый взгляд порождает ту же проблему: решение квадратного уравнения. Там, по идее, тоже нужны var-параметры: надо получить 3 вещественных числа и вернуть 2 числа + количество корней. Когда-то в Паскале, кажется, я писал для этого функцию: 2 var-параметра x1 и x2, а число корней возвращалось в результате. Однако здесь есть существенное отличие: квадратный полином является осмысленной, полезной сущностью, более того, частным случаем полинома произвольной степени. Для ООП-языка тут особо спорить не о чем: конечно, надо создать класс «полином» и предоставить методы, возвращающие его корни и количество корней (может быть, с отдельным безаргументным методом вычисления корней). Но одно дело — полином, и совсем другое — некая сугубо частная штука: комбинация минимума, максимума и их индексов.
А почему всем так не нравятся LongHolder-ы? Ведь достаточно часто методы Java API возвращают результат в своем изменяемом аргументе-массиве: например, ByteBuffer.get. Есть и такой класс, скажем, как java.text.ParsePosition. Чем LongHolder-ы хуже? Ведь это просто ссылка на long, так же как long[] — ссылка на массив long-ов.
Я не первый раз сталкиваюсь с описанной проблемой. Пример из совсем другой области (геометрии): мне нужно было найти координаты двух точек пересечения некоторой прямой и 3-мерного тела. В Паскале я, не задумываясь, применил бы var-параметр. А в Java что? Заводить класс «пара точек»? Или возвращать массив из 2 элементов типа Point, после чего работать с «магическими индексами» 0 и 1? По идее, тут бы передать 2 ObjectHolder-а, причем если нужной точки пересечения нет, записать туда null. Плохо только, что ObjectHolder до сих пор не генерализован.
- От редактора
