При вызове метода (функции) ему должны быть переданы аргументы.
Есть два принципиально различных способа передать аргумент.
Первый - скопировать значение аргумента в область параметра вызываемого
контекста. Такой способ называется "передачей аргумента по значению".
Изменения параметра, переданного по значению, никак не отразятся на аргументе,
поэтому такой способ безопасен. Однако, копирование не всегда эффективно,
например, когда надо передать массивный объект. В этом случае в качестве
аргумента выступает адрес структуры, которую следовало бы передать в
распоряжение вызываемого метода. Используя адрес, метод получает доступ к
структуре и может изменить ее состояние, которое сохранится и после возврата
потока управления в вызывающий контекст. Это - второй способ, называемый
передачей "аргумента по ссылке", потому что аргумент, как таковой, в область
параметров не передается, а поступает в распоряжение метода посредством своего
адреса. Адрес обычно хранится во вспомогательной переменной, например, для
"С++" - в ссылке или указателе, а для ассемблера имеется примерно пять
вариантов. Очевидно, что форма, в которой передается адрес, не имеет
принципиального значения, - важна суть. Эта была общая картина, теперь
посмотрим, что мы имеем в Java.
Java: передача по ссылке?
Передача аргументов в Java происходит только по значению: аргумент копируется
в область параметров и - точка!
Однако, все так ясно только для аргументов примитивного типа, таких как int или
float. Если же аргументами являются объектные ссылки, картина выглядит как
классическая "передача по ссылке". Несмотря на то, что в метод передается копия
ссылки, объект ссылки может изменить свое состояние в вызванном методе, причем
это изменение сохранится и после возврата в вызывающий контекст. Естественно,
передаваемая копия ссылки может меняться как угодно и это не повлияет на
оригинал, находящийся в вызывающем контексте - оригинал по-прежнему будет
указывать на тот же самый объект, что и до вызова. Но это не достижение Java
и даже не отличительная черта, поскольку в С++ ссылка на объект, вообще
является константной - ее нельзя переназначить, а следовательно, как ни
передавай ее в метод: оригиналом или копией, в вызывающем контексте она
утрачена не будет.
Итак, повторим, ситуация выглядит как классическая передача по ссылке, хотя
утверждается, что в Java такого способа не существует.
Так есть или нет?! Этот вопрос решается как обычно: одни авторы - говорят, что -
есть, другие,- нет, а третьи,- считают эту путаницу особым видом передачи
аргументов в Java. Но кто же прав?!
Java создавался группой разработчиков,
двое (по меньшей мере) из которых, пишут книги. Это П.Ноутон ("Java 2", 2000г.)
и Дж. Гослинг ("Язык программирования Java", 2001г.). П.Ноутон считает, что в
Java имеет место передача объектов по ссылке, а Дж. Гослинг - категорически
против. Как же Дж. Гослинг обосновывает свою точку зрения?
В его книге на стр. 34 мы читаем следующее:
"Доступ к объектам осуществляется
посредством ссылок - любая переменная, которая, как может показаться на первый
взгляд, содержит сам объект, на самом деле хранит ссылку на этот объект. … В
большинстве случаев вы вольны отступать от точной трактовки различий между
действительными объектами и ссылками на них. Вполне допустимо вместо фразы
"Передать в качестве аргумента метода ссылку на объект" употребить
"Передать методу объект".
Логически, отсюда прямо следует, что мы именно "передаем объект по ссылке" и
по форме и по сути. Тем не менее, в разделе, посвященном параметрам метода
(стр. 77 ), мы читаем противоположное:
"Бытует насколько распространенное,
настолько же неверное суждение о том, что объекты Java якобы передаются
"по ссылке". В теории языков программирования достаточно употребительный
термин передача по ссылке, строго говоря, означает следующее: когда аргумент
передается в функцию, вызванная функция получает ссылку на исходное значение,
но не копию этого значения".
Дж. Гослинг здесь описывает, что означает передача по ссылке, но отказывается
замечать, что из его же описания прямо и логично следует, что в Java передача
объектов осуществляется именно по ссылке. Но все же, "краем глаза" он видит
несообразицу между посылкой и заключением и начинает излагать свое понимание термина
"передача по ссылке". Суть его пространных рассуждений очень трудно уловить, но она
сводится к следующему: в Java нет передачи по ссылке, потому что сама
объектная ссылка передается по значению. Иначе говоря, из того, что в Java
ссылка передается не по ссылке (?!), следует, что в Java нет передачи по ссылке.
(Это я оставлю без комментариев)
Вероятно, автор(ы), цитируемой книги слышал и твердо верит, что в Java нет
передачи по ссылке,
но логически обосновать это - не в состоянии. Действительно, чисто
логически проблема нерешаема, - здесь причиной коллизии является концептуальное
расхождение между С++ и Java.
В Java объект - это не переменная, что является центральной идеей в
С++. В языке Java и, соответственно, в тексте программы, объект (или экземпляр
класса, если хотите) не имеет обозначения.
То, что выглядит в тексте как определение или использование объекта, - есть
явное определение и применение объектной ссылки. ("выглядит" для
бывших программистов на С++, которых среди программистов на Java, наверное,
90%. О том, как это выглядит для программистов иной этиологии, мне трудно
судить). Поэтому нет никакой принципиальной разницы в работе с объектной ссылкой в
случае, когда поток управления все время находится в одном контексте
и случаем, когда, в результате вызова метода, происходит смена контекста.
Апология Дж. Гослинга и чистых Явистов
Участники дискуссии, как с одной, так из другой стороны, упустили из виду то,
что передача по ссылке требует выполнения следующих условий:
- Существует возможность создать ссылку на переменную.
- Существует прочно связанная пара переменных, одна из которых -
ссылка на другую. Один член пары - передается посредством другого.
- Для аргумента данного вида (типа) передача по ссылке является антиподом передачи
по значению, ввиду чего параметр-ссылка имеет отличие в виде особого
знака, например, в C++ применяются * или &)
Посмотрим, соблюдаются ли эти условия в Java:
- Принципиально невозможно создать ссылку на переменную какого-либо
вида (типа), будь то примитив или объектная ссылка и, тем более,
передать ее в метод.
- Не существует пары переменная - ссылка. Объектная ссылка не содержит
адрес какой-либо другой переменной, следовательно передавая
в метод объектную ссылку, мы не передаем ничего, кроме самой объектной
ссылки.
- Для аргумента данного вида (типа) имеется только один способ передачи в
область параметров - путем копирования его значения. Соответственно,
не требуется каких-либо особых знаков для различения обычных параметров
и параметров-ссылок.
Как мы видим, в Jаva не существует ссылки как таковой, поэтому здесь "передача
аргумента по ссылке" - бессмысленный набор слов: объекты Java - не
переменные, они не могут быть аргументами, они - вообще не передаются в метод,
ни путем копирования объекта, ни через ссылку.
При переходе с С++ можно конечно "натянуть" на Java свое
понимание С++, но когда пишешь учебник, надо, все же, изучить спецификацию
языка.
Может показаться, что вреда в этом ошибочном мнении немного, однако,
работать с одним языком, считая его другим - все равно, что ходить с
завязанными глазами.
Тем не менее, такие коллизии помогают лучше понять философию Java: в тексте Java-программы
ты видишь
буквально то, что видишь. К такому же выводу мы пришли и в уроке
"Оператор инкремента"