Список параметров метода Java varargs и массив - PullRequest
34 голосов
/ 23 марта 2011

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 вместо одного параметра массива? Кажется, что вы добавили немного гибкости звонящего бесплатно.

Может ли эксперт поделиться внутренней разницей JVM, если она есть?

Спасибо.

Ответы [ 6 ]

36 голосов
/ 23 марта 2011

Массивы существовали с самого начала 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) { ... }

Изменение порядка параметров может технически решить эту проблему, однако это нарушает код клиента.

Обновление: Effective 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, приведенный выше код компилируется без предупреждений, и непредвиденный результат выглядит примерно так: "[[I@3e25a5]".

8 голосов
/ 23 марта 2011

Основная причина не указывать все как varargs в том, что это не всегда имеет смысл.Например, если InputStream.read(byte[]) где определено как `read (byte ...), тогда будет допустим следующий вызов:

myInputStream.read(0, 1, 2, 3);

Это создаст 4-элементный байтовый массив, передайте это и затем отбросьте это.

5 голосов
/ 23 марта 2011

vararg - это простой синтаксический сахар для массива.

если вы позвоните foo("abc", "def", "ghi"); тогда компилятор будет называть это foo(new String[] {"abc", "def", "ghi"});

компилятор создаст один новый массив и передаст его foo(). Нельзя иметь и foo(String...), и foo(String[]). Поскольку оба функционально одинаковы.

2 голосов
/ 23 марта 2011

Вот как определяются varargs. Расширение varargs не делает каждую функцию, принимающую массив, функцией varargs. Вы должны позвонить в бар так:

bar(new String[]{"abc", "def", "ghi"})
2 голосов
/ 23 марта 2011

в foo вы указываете три параметра, вам придется вызывать bar следующим образом:

 bar(new String[]{"abc", "def", "ghi"});

, чтобы вызывать его только с одним параметром, то есть String [], в этом случае это почтине имеет ничего общего с внутренними компонентами, ваша сигнатура метода для панели методов просто утверждает, что он имеет только один параметр, тогда как foo имеет n параметров, которые являются строками

1 голос
/ 20 августа 2016

Другое отличие - эффективность. Объекты, которые находятся внутри явного массива, не будут вызваны. Однако параметры списка переменных аргументов оцениваются, когда метод помещается в стек.

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

Пример: someMethod (Object ... x) anotherMethod (Object []);

someMethod (a (), b (), c ()); // a, b и c будут вызваны до того, как вы попадете в метод.

anotherMethod (new Object [] {a (), b (), c ()}); // Методы не вызываются до доступа к объектам.

...