Кажущиеся несовместимыми правила «Pass By Value». Был ли выбор int [] просто синтаксическим сахаром, чтобы объекты массива выглядели как массивы C / C ++? - PullRequest
0 голосов
/ 11 августа 2009

Я прочитал что-то онлайн, в котором неправильно указывалось, что стандартные массивы int [] и т. Д. В Java передавались как копии, а не как ссылки на массивы, по аналогии с базовыми числовыми типами, и заканчивали тем, что переписывали массив, когда я думал Я модифицировал копию. Могу ли я объяснить это как выбор дизайна, чтобы сделать вещи проще для целевой аудитории Java примерно в середине 90-х годов? (заставляет объекты выглядеть так же синтаксически, как массивы C, или массивы действительно не имеют тип "Object" в Java?)

То есть, почему они просто не сделали что-то вроде:

Array array = new Array(<size>);

Кроме того, почему они не сделали все (кроме литералов) по ссылке для обеспечения согласованности? (int s будет затем передано как ссылка на int, а не как значение int, поэтому изменение переменной, являющейся аргументом метода в этом методе, приведет к изменению значения исходной переменной и т. Д. .)

Ссылка на обсуждение передачи по ссылке и передачи по значению в Java

Ответы [ 6 ]

12 голосов
/ 11 августа 2009

Ничего не передается по ссылке в Java - но вам нужно знать, каково значение переменной.

Значением переменной типа int[] является , а не массив. Это ссылка на массив. Все массивы являются ссылочными типами, даже если это массив примитивов.

Когда вы пишете:

int[] x = { 1, 2, 3, 4, 5 };
someMethod(x);

затем значение из x передается по значению в someMethod. Это ссылка на массив - метод может изменить содержимое массива, но он не сможет изменить значение самого x, что он мог бы сделать, если бы в Java использовалась передача по ссылке.

Как только вы поймете, что значение любого выражения является либо ссылочным, либо примитивным значением, многие вещи становятся понятнее. Например, возьмите этот код:

int[] x = { 1, 2, 3, 4, 5 };
int[] y = x;
y[0] = 10;
System.out.println(x[0]);

Каким вы ожидаете получить результат? Оператор присваивания копирует значение из RHS в LHS, поэтому x и y являются ссылками на один и тот же массив. Поэтому в последней строке выводится 10. Это «копирование значения» в точности соответствует принципу, используемому для передачи аргументов.

Здесь нет противоречий - вам просто нужно понять, что происходит, и что на самом деле означают передача по значению и передача по ссылке.

РЕДАКТИРОВАТЬ: Я действительно удивлен, есть еще сомнения по этому поводу.

Из спецификации языка Java, раздел 8.4.1 (формальные параметры) :

Когда вызывается метод или конструктор (§15.12), значения фактических выражений аргумента инициализируют вновь созданные переменные параметра, каждый из объявленного типа, перед выполнением тела метода или конструктора

Это практически определение семантики передачи по значению: значения аргументов используются в качестве начальных значений параметров.

Эти значения могут быть ссылками или примитивными значениями - это не меняет того факта, что передается значение .

РЕДАКТИРОВАТЬ: определение, которое вы используете, хотя и плохо выражено, на мой взгляд, безусловно, не означает, что утверждение, что Java передает все по значению, вводит в заблуждение.

Это говорит, для передачи по значению:

Это делает копию переменной и может быть довольно медленным и неэффективным, если переменная большая, как массив.

Обратите внимание, что речь шла о C и C ++ (хотя в C ++ действительно также есть передача по ссылке). В нем говорится о создании копии переменной . Это именно то, что делает Java. Разница в том, что в Java значение переменной всегда либо примитивное значение, либо ссылка. Это никогда не объект.

Как только вы поймете, что критическая точка (и это важно не только для передачи аргументов), очень просто правильно сказать, что Java передает все по значению. Когда вызывается метод или конструктор, аргументы оцениваются, и значения используются в качестве начальных значений параметров. Это передача по значению, и именно это делает Java для всех параметров.

Что касается PDF, на который вы ссылаетесь - это позорно плохое обсуждение темы, ИМО. Это путает то, что передается по ссылке, это особые случаи String без веской причины (String бывает неизменным, но могут быть и другие классы), и это в целом неправильно. Поможет ли мне дать несколько ссылок, поддерживающих мою точку зрения?

Есть еще сотни. Вы не первый, кого это смущает, но Java действительно передает все по значению, даже ссылки.

7 голосов
/ 11 августа 2009

Все в Java передается по значению. Все. В случае типов объектов (которые являются массивами) то, что передается по значению, является ссылкой. Нет, это не то же самое, что переход по ссылке.

2 голосов
/ 11 августа 2009

Да, конечно. Нельзя отрицать, что адрес памяти также является значением. Таким образом, передача по ссылке на самом деле является частным случаем передачи по значению.

+ 1 для большинства филистимских ответов на SO.

Кстати, я думаю, что у меня тоже есть часть ответа на

"почему они просто не делали все по ссылке для обеспечения согласованного поведения?"

В этом случае, как система работает с вызовами, где аргументы являются литералами, скажем, 'result = f (2);'

Должен ли этот литерал также передаваться по ссылке? И если это так, не откроет ли это возможность того, что ваши литералы изменят значение с помощью какого-либо вызова, который обновляет ссылку? В каком случае термин «литерал» может стать несколько неуместным, и многие возможные оптимизации кода станут невозможными для реализации компилятором?

1 голос
/ 11 августа 2009

По крайней мере, с точки зрения производительности, имеет смысл передавать массивы по ссылке. В противном случае вам понадобится много ненужного копирования.

Сказав это, я согласен с тем, что должен существовать неизменяемый объект массива.

1 голос
/ 11 августа 2009

Да, int[] - это Объект. Да, синтаксис заключался в том, чтобы привлечь программистов на C / C ++. Нет, это не плохой выбор дизайна, два утверждения не противоречат друг другу.

0 голосов
/ 11 августа 2009

Это распространенное заблуждение о том, что означает, что объекты Java «передаются по значению». ВСЕ Java-объекты, массивы и примитивы передаются по значению, но в случае объектов и массивов передаваемое значение фактически является указателем (или, если хотите, ссылкой) на исходный объект.

Вот разница. В Java и C ++ я могу написать эту функцию:

void myFunc(int[] arr) {
    arr[0] = 17;
}
myFunc(myLocalArray);

Это означает, что на любом языке значение, хранящееся в arr [0], равно 17 как внутри переменной myRunc "arr", так и внутри myLocalArray.

Это, конечно, очень захватывающе, но никак не связано с «передачей по ссылке».

Передача массива по ссылке означает, что на языке, подобном C ++ (но НЕ в Java), я могу сделать это:

void myFunc(int[] arr) {
    arr = null;
}
myFunc(myLocalArray);

Здесь я полностью заменил объект myLocalArray совершенно другим объектом (в данном случае пустым, но это может быть что угодно). Я не просто изменил объект (например, изменил значение, хранящееся в определенном индексе), но скорее стер все свидетельства того, что myLocalArray когда-либо даже указывал на допустимый массив.

На Java это невозможно сделать.

Итак, основное правило в Java:

  1. Все правки внутри объекта (например, вызовы функций, которые изменяют состояние, прямые изменения государства, изменение значений в массиве, et и т.д.) действует на исходный объект (т. е. в вызывающей функции). Ни одна копия не сделана без тебя специально делая это.
  2. Полностью перезаписать локальный объект новый НИКОГДА не повлияет на объект в вызывающей функции.
...