2010-01-29 2 views
19

Если я пытаюсь написать метод, как показано нижеПочему varargs должен быть последним в сигнатуре метода?

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

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

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

Я не совсем понимаю, что требование типа переменных аргумента является последним. Любые входы будут полезны.

+0

Как правило, ответ «Потому что те являются правила». Правила - это правила.Почему это важно * почему * существуют правила? Какая у вас проблема? –

+3

@ S.Lott: Я согласен, но мне все еще любопытно, насколько обосновано решение Sun. –

+0

Всякий раз, когда я вижу сообщение об ошибке, я получаю такое чувство, что делаю что-то неправильно. Что я делаю неправильно здесь, кроме нарушения правила? –

ответ

18

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

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

+0

Хм .. может быть, это то, о чем говорил С. С. Лотт. –

+0

Он немного упрощен (отсюда «концептуально при смещении 0»). На практике у вас есть только один стек, который также содержит разливы регистра, обратные адреса и т. Д. – MSalters

+0

. Причиной ... быть последним в Java является «потому что это правило». Причиной ... быть последним в C является безумный кусок мелодий C-компилятора. «Почему» не помогает. Правило просто есть. –

8

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

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

Или даже:

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

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

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

+0

+1, жизнь не может быть более простой :) –

20

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

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

"тест", "тест", "тест", "тест"

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

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

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

Однако, когда это наоборот

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

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

+6

Теоретически это было бы технически возможно. Техническая проблема возникает только при использовании 3 или более аргументов, и varargs не является первым или последним из них. – BalusC

+3

@Balus: до сих пор нет двусмысленности, даже когда varargs находится посередине, только сложность реализации в компиляторе, но теоретически это возможно. Реальная проблема возникает, когда у вас есть несколько терминов vararg. Тогда вы должны иметь такие вещи, как возврат к решению, что подразумевается. Это в основном становится так же трудно разрешить, как соответствие регулярному выражению. –

+3

сделал бы интересную языковую функцию: pass-by-regexp. 'void foo (^ ([0-9] *) (A | B.) (bar)? $)' – MSalters

3

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

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

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

+0

Да, звучит очень логично –

+0

@ Дафф, я думаю, что ваш пример не имеет ничего общего с varargs. Даже если этот метод: public void someStuff (Object args, String a), компилятору еще предстоит решить, следует ли использовать: someStuff (Object, String) или someStuff (String, String). Или это я пропустил в ваших объяснениях? – sateesh

+0

Хм, я не знал, объяснил ли я это наилучшим образом, и я не утверждаю, что это правильное объяснение. В основном это в основном совместимость, поскольку varargs были добавлены позже в java-языке и рассматриваются как массив. Поэтому я предполагаю, что они не хотели менять алгоритмы поиска для выбора правильного метода для вызова, который вам не нужен, когда вы можете убедиться, что только последний параметр может быть vararg. – Daff

0

Учитывая, как используется метод с 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 все прозвища и фамилия отсутствуют? Снова эта неоднозначность может быть решена, но ценой неуклюжего дополнительного синтаксиса.

+0

Неоднозначность может быть легко устранена, указав, что все параметры без vararg должны присутствовать и сопоставляться слева или справа в списке параметров, а остальные 0 или более параметров составляют vararg. Во втором примере действительно трудно сказать, что у Джона нет никнейма, но тогда это просто плохой метод параметров метода: если можно ожидать, что список параметров может быть пустым, его нельзя размещать посередине. – Ingo

0

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

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

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

Мы не можем объявить несколько 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 метода оных должен быть последним параметр