2010-01-29 9 views
19

Si trato de escribir un método como el de abajo¿Por qué varargs debería ser el último en la firma del método?

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

consigo este error

La variable tipo de argumento Objeto del someStuff método debe ser el último parámetro.

No entiendo completamente el requisito de que el tipo de argumento variable sea el último. Cualquier entrada será útil.

+0

En general, la respuesta es "porque esas son las reglas". Las reglas son las reglas.¿Por qué importa? ¿Por qué * las reglas existen? ¿Qué problema tienes? –

+3

@ S.Lott: Estoy de acuerdo, pero todavía tengo curiosidad sobre la razón de ser de la decisión de Sun. –

+0

Cada vez que veo un mensaje de error tengo la sensación de que estoy haciendo algo mal. ¿Qué estoy haciendo mal aquí aparte de romper la regla? –

Respuesta

18

Sigue la convención C.La convención C a su vez se basa en arquitecturas de CPU que pasan argumentos en la pila. Los primeros argumentos no variables terminan en un desplazamiento fijo en el marco de pila. Si pudieras poner primero los argumentos vararg, el desplazamiento de la pila de los siguientes argumentos dependería de cuántos parámetros vararg hubieras pasado. Esto complicaría en gran medida la cantidad de código necesario para acceder a ellos.

En su ejemplo, con String a primero, conceptualmente está en offset 0 independiente de la cantidad de argumentos vararg que siguen. Pero con String a pasado, podría estar en el desplazamiento 0, 4, 8, 12, etc. - tendría que calcular args.size * 4 cada vez que necesitara String a.

+0

Hmm ... quizás esto es a lo que @ S.ott se estaba refiriendo. –

+0

Es, aunque ligeramente simplificado (de ahí el "conceptualmente en offset 0"). En la práctica, solo tiene una pila que también contiene derrames de registros, direcciones de devolución, etc. – MSalters

+0

Lo es. El razonamiento para ... ser el último en Java es "porque esa es la regla". La razón de ... ser el último en C es una pieza insana de trivialidades del compilador C. El "por qué" no es útil. La regla simplemente es. –

8

Porque eso haría innecesariamente complejo el lenguaje. Imagínese si también permite otras sintaxis:

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

O incluso:

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

Esta segunda sintaxis significa una cadena seguido de cualquier número de argumentos de tipo Object, seguido de un número entero, seguido de más objetos . Claro que puedes diseñar un lenguaje que acepte cosas como esa, pero ¿y si también quisieras especificar que el args2 debe contener al menos un elemento, pero args puede estar vacío? ¿Por qué no podemos hacer eso también? Usted podría diseñar ese tipo de lenguaje.

Todo se reduce a, ¿qué tan complicadas quieres que sean las reglas? En este caso, eligieron una opción simple que satisface las necesidades.

+0

+1, la vida no puede ser más simple :) –

20

El argumento variable tiene que ser el último para que el compilador pueda determinar qué argumento es cuál.

Por ejemplo, dicen que pase

"prueba", "test", "test", "prueba"

en su función

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

Java puede no funcionar si quieres la variable args contiene 3 o 4 cadenas. Puede ser obvio para ti al momento de escribir, pero es ambiguo.

Sin embargo, cuando es al revés

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

El compilador de Java ve la primera cadena, pegarlo en "una" y luego sabe que las cadenas restantes pueden utilizarlos de manera segura args y no hay ambigüedad sobre las variables.

+6

Teóricamente, sería técnicamente posible. El problema técnico solo comienza cuando usa 3 o más argumentos y varargs no es el primero ni el último de ellos. – BalusC

+3

@Balus: Todavía no hay ambigüedad incluso cuando los varargs están en el medio, solo una dificultad de implementación en el compilador, pero en teoría es posible. El problema real viene cuando tienes más de un término vararg. Entonces tienes que tener cosas como retroceder para resolver lo que significa. Básicamente se vuelve tan difícil de resolver como la coincidencia de expresiones regulares. –

+3

haría una función de lenguaje divertido: pass-by-regexp. 'void foo (^ ([0-9] *) (A | B.) (bar)? $)' – MSalters

3

Well a String es también una instancia de Object, por lo que si está usando varargs su matriz vararg tiene que ser el último parámetro porque el compilador no puede decidir realmente qué es args y cuál es su cadena a. Piense en la llamada al método como una tupla del nombre del método y una lista de objetos que son sus parámetros. Si tiene dos métodos de este modo:

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

El compilador no podía decidir qué método elegir para someStuff ("Hola", "Hola"). Si coloca su String a como primer argumento, puede decidir que someStuff (String, String) sea más específico que someStuff (String, Object).

+0

Sí, suena muy lógico –

+0

@Daff, creo que su ejemplo no tiene nada que ver con varargs. Incluso si el método es: public void someStuff (Object args, String a) el compilador aún tiene que decidir si usar: someStuff (Object, String) o someStuff (String, String). ¿O es que extraño algo en tu explicación? – sateesh

+0

Hm ok No sabía si lo había explicado de la mejor manera y no afirmo que esta es LA explicación correcta. Básicamente es principalmente una cuestión de compatibilidad ya que los varargs se han agregado más adelante en el lenguaje Java y se tratan como una matriz. Así que supongo que no querían cambiar los algoritmos de búsqueda para elegir el método correcto para llamar, que no es necesario, cuando puedes estar seguro de que solo el último parámetro puede ser un vararg. – Daff

0

Dado que se usa un método con var args, cualquier otro formato podría generar ambigüedades. Tener los varargs últimos evita posibles ambigüedades sin requerir sintaxis adicional para resolver las ambigüedades que disminuirían el beneficio de la característica.

en cuenta la declaración de método siguientes:

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

Cuando se utiliza como varargsAreCool("John", "Smith") es obvio que no tiene John Smith apodos. Cuando se usa así varargsAreCool("Andrew", "Jones", "The Drew", "Jonesy").

Consideremos ahora la siguiente declaración de método inválida:

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

Cuando se utiliza como varargsAreCool("John", "Smith") es Smith apodo de John o su apellido? Si es su apellido, ¿cómo indico que no tiene apodos? Para hacer esto, probablemente tendrías que usar el método como este varargsAreCool("John", new String[]{}, "Smith"), que es torpe y en cierto modo frustra el propósito de la función.

Cuando se utilizan así varargsAreCool("Andrew", "The Drew", "Jonesy", "Jones") son The Drew, Jonesy and Jones todos los apodos y el apellido ¿nombre? Una vez más, esta ambigüedad podría resolverse, pero a costa de una sintaxis adicional torpe.

+0

Las ambigüedades pueden resolverse fácilmente indicando que todos los parámetros no vararg deben estar presentes y que coinciden desde la izquierda o desde la derecha en la lista de parámetros, y los 0 o más parámetros restantes forman la vararg. En su segundo ejemplo, es realmente difícil decir que John no tiene apodo, pero entonces es simplemente un mal diseño de parámetro de método: si uno puede anticipar que la lista de parámetros podría estar vacía, no debe colocarla en el medio. – Ingo

0

Es la quinta regla de VAR-args por GeekOnJava artículo:

Para preparar objeto de matriz como se muestra en el programa anterior, si queremos de declarar var-args como primero o segundo parámetro el compilador no tendrá pista cuántos argumentos deben incluirse en el objeto de matriz.

Y de acuerdo con la quinta regla:

No podemos declarar múltiples parámetros var-arg a pesar de que son diferentes tipos. Debido a que el compilador no tiene idea de cuántos valores primero se debe crear el objeto var-arg array. Esto lleva a error de tiempo de compilación.

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

en el compilador escenario anterior dice -

La variable tipo de argumento int del complemento método debe ser el último parámetro

Cuestiones relacionadas