Список параметров var var var vs. array
Varargs:
public static void foo(String... string_array) { ... }
против
Параметр одиночного массива:
public static void bar(String[] string_array) { ... }
Java 1.6, похоже, принимает/отклоняет следующее:
String[] arr = {"abc", "def", "ghi"};
foo(arr); // accept
bar(arr); // accept
foo("abc", "def", "ghi"); // accept
bar("abc", "def", "ghi"); // reject
Предполагая, что приведенное выше верно/правильно, почему не всегда использовать varargs вместо параметра single array? Кажется, что бесплатно добавьте гибкость звонящего.
Может ли эксперт делиться внутренней разницей JVM, если есть один?
Спасибо.
Ответы
Ответ 1
Массивы были с самого начала Java, а varargs - довольно недавнее дополнение. Таким образом, многие старые коды по-прежнему используют массивы.
Обратите также внимание на то, что вызов общего метода vararg с явным параметром массива может приводить к разному поведению, чем ожидалось:
public <T> void foo(T... params) { ... }
int[] arr = {1, 2, 3};
foo(arr); // passes an int[][] array containing a single int[] element
Таким образом - помимо требующих больших усилий без явной выгоды - не всегда желательно заменять устаревшие параметры массива на varargs.
Не говоря уже о случаях, когда вы не можете, потому что в списке параметров метода есть еще один параметр:
public void foo(String[] strings, String anotherParam) { ... }
Переупорядочение параметров может технически решить эту проблему, однако оно нарушает клиентский код.
Обновление: Эффективная Java 2nd. Издание, пункт 42: Использование varargs разумно объясняет это более подробно, давая также конкретный пример: Arrays.asList()
был модифицирован в Java5, чтобы иметь параметры vararg, которые случайно побила много существующего кода, может вызвать неожиданности при использовании этой (теперь устаревшей) идиомы для печати массива:
System.out.println(Arrays.asList(myArray));
Update2: Двойной проверил источник, и он говорит, что проблема возникает с массивами примитивных типов, например int[]
. Перед varargs, код выглядит следующим образом:
int[] digits = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 4 };
System.out.println(Arrays.asList(digits));
будет генерировать ошибку компиляции, потому что только массивы ссылочных типов могут быть преобразованы в List
. Начиная с varargs и дооснащая asList
, код выше компилируется без предупреждений, а непреднамеренный результат - это что-то вроде "[[[email protected]]"
.
Ответ 2
Основная причина не указывать все как varargs в том, что это не всегда имеет смысл. Например, если InputStream.read(byte[])
, где определено как` read (byte...), тогда будет выполнен следующий вызов:
myInputStream.read(0, 1, 2, 3);
Это создало бы 4-элементный массив байтов, передал бы его и отменил бы его.
Ответ 3
vararg - простой синтаксический сахар для массива.
если вы вызываете foo("abc", "def", "ghi");
, тогда
компилятор будет называть его foo(new String[] {"abc", "def", "ghi"});
компилятор создаст один новый массив и передаст его в foo()
.
Нельзя иметь как foo(String...)
, так и foo(String[])
. Поскольку оба функционально одинаковы.
Ответ 4
в foo вы указываете три параметра,
вам нужно будет вызвать бар следующим образом:
bar(new String[]{"abc", "def", "ghi"});
так что вы вызываете его только одним параметром, то есть строкой []
в этом случае это не имеет ничего общего с внутренними, ваша сигнатура метода для панели методов просто утверждает, что она имеет только один параметр, тогда как foo имеет n параметров, которые являются всеми строками
Ответ 5
Это то, как определяются varargs. Расширение varargs не делает каждый массив, принимающий функцию, функцией varargs. Вы должны вызвать бар следующим образом:
bar(new String[]{"abc", "def", "ghi"})
Ответ 6
Еще одно отличие - эффективность. Объекты, находящиеся внутри явного массива, не будут вызваны. Однако параметры списка переменных аргументов вычисляются, когда метод помещается в стек.
Это видно, когда вызов функции передается как параметр, который возвращает тип, который используется в списке аргументов переменных.
Пример:
someMethod (Object... x)
anotherMethod (Object []);
someMethod (a(), b(), c());//a, b и c будут вызваны, прежде чем вы войдете в метод.
anotherMethod (новый объект [] {a(), b(), c()});//Эти методы не вызываются до тех пор, пока не будут доступны объекты.