Почему varargs должен быть последним в сигнатуре метода? - PullRequest
21 голосов
/ 29 января 2010

Если я попытаюсь написать метод, как показано ниже

public void someStuff(Object ... args, String a )

Я получаю эту ошибку

Тип аргумента переменной Object метода someStuff должен быть последним параметром.

Я не совсем понимаю требование типа аргумента переменной быть последним. Любые материалы будут полезны.

Ответы [ 6 ]

22 голосов
/ 29 января 2010

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

Например, скажем, вы передаете

«тест», «тест», «тест», «тест»

в вашу функцию

public void someStuff(Object ... args, String a)

Java не может работать, если вы хотите, чтобы переменная args содержала 3 или 4 строки. Это может быть очевидно для вас во время написания, но это неоднозначно.

Однако, когда все наоборот

public void someStuff(String a, Object ... args)

Компилятор Java видит первую строку, вставляет ее в «a», а затем знает, что оставшиеся строки можно безопасно поместить в аргументы и нет никакой неопределенности в отношении переменных.

21 голосов
/ 29 января 2010

Это следует за C соглашением.Соглашение C, в свою очередь, основано на архитектурах ЦП, которые передают аргументы в стек.Первые не-vararg аргументы заканчиваются фиксированным смещением в стеке.Если бы вы могли поместить аргументы vararg первыми, смещение стека следующих аргументов будет зависеть от того, сколько параметров vararg вы бы передали.Это значительно усложнило бы количество кода, необходимого для доступа к ним.

В вашем примере, сначала с String a, концептуально это со смещением 0, независимо от количества последующих аргументов vararg.Но с String a last это может быть смещение 0, 4, 8, 12 и т. Д. - вам придется вычислять args.size * 4 каждый раз, когда вам нужно String a.

9 голосов
/ 29 января 2010

Потому что это сделает язык излишне сложным. Представьте, если вы также разрешаете другие синтаксисы:

public void someStuff(String a, Object ... args, String b)
{
}

Или даже:

public void someStuff(String a, Object ... args, int b, Object ... args2)
{
}

Этот второй синтаксис означает строку, за которой следует любое количество аргументов типа Object, за которыми следует целое число, а затем еще несколько объектов. Конечно, вы могли бы разработать язык, который мог бы принимать такие вещи, но что, если вы также хотите указать, что args2 должен содержать хотя бы один элемент, но args может быть пустым? Почему мы не можем сделать это тоже? Вы могли бы создать такой язык.

Это сводится к тому, насколько сложными вы хотите, чтобы правила были? В этом случае они выбрали простой вариант, который удовлетворяет потребности.

3 голосов
/ 29 января 2010

Ну, а String также является экземпляром Object, поэтому, если вы используете varargs, ваш массив vararg должен быть последним параметром, потому что компилятор не может действительно определить, что такое args, а какая строка a Думайте о вызове метода как о кортеже имени метода и списка объектов, которые являются вашими параметрами. Если у вас есть два метода, таких как:

public void someStuff(Object ... args, String a )
public void someStuff(String a, String b)

Компилятор не может решить, какой метод выбрать для someStuff («Hello», «Hello»). Если вы указали в качестве первого аргумента строку String, он может решить, что someStuff (String, String) более конкретен, чем someStuff (String, Object).

0 голосов
/ 03 августа 2015

Пятое правило вар-аргов от GeekOnJava статья:

Чтобы подготовить объект массива, как показано в приведенной выше программе, если мы хотим объявить var-args как первый или второй параметр компилятора не будет Укажите, сколько аргументов должно быть включено в объект массива.

А согласно пятому правилу:

Мы не можем объявить несколько параметров var-arg, даже если они разных типов. Потому что компилятор не знает, сколько значений должен быть создан первый объект массива var-arg. Это приводит к ошибке времени компиляции.

public void add(int... a,int...b){}//leads compile time error
public void add(int... a,long...b){} //compile time error

в приведенном выше сценарии компилятор говорит -

Тип аргумента переменной int метода add должен быть последним Параметр * +1021 *

0 голосов
/ 29 января 2010

Учитывая, как используется метод с var args, любой другой формат может привести к неоднозначности. Наличие последних переменных varargs предотвращает возможные неоднозначности, не требуя дополнительного синтаксиса для устранения неоднозначностей , что уменьшило бы преимущества функции.

Рассмотрим следующее объявление метода:

public void varargsAreCool(String surname, String firstname, 
                           String... nicknames) {
    // some cool varargs logic
}

Когда используется как varargsAreCool("John", "Smith"), очевидно, что John Smith не имеет ников. При использовании так varargsAreCool("Andrew", "Jones", "The Drew", "Jonesy").

Теперь рассмотрим следующее недопустимое объявление метода:

public void varargsAreCool(String surname, String... nicknames,
                           String firstname) {
    // some cool varargs logic
}

При использовании как varargsAreCool("John", "Smith") это Smith ник Джона или его фамилия? Если это его фамилия, как я могу указать, что у него нет ников? Чтобы сделать это, вам, вероятно, придется использовать такой метод, как этот varargsAreCool("John", new String[]{}, "Smith"), который неуклюж и несколько отрицает цель этой функции.

При таком использовании varargsAreCool("Andrew", "The Drew", "Jonesy", "Jones") все ли псевдонимы The Drew, Jonesy and Jones и фамилия отсутствуют? Опять эта неоднозначность может быть решена, но за счет неуклюжего дополнительного синтаксиса.

...