¿Cuándo usas varargs en Java?

192

Tengo miedo de los varargs. No sé para qué usarlos.

Además, se siente peligroso dejar que las personas pasen tantos argumentos como quieran.

¿Cuál es un ejemplo de un contexto que sería un buen lugar para usarlos?

Harry Quince
fuente
3
No veo por qué sería "peligroso". No es más peligroso que tener un método llamado muchas veces con diferentes argumentos. ¿Tienes preocupaciones con la pila? Entonces no debería, porque los varargs se asignan a matrices que se pasan por referencia.
jfpoilpret
66
Solo quería intervenir: no hay nada de malo en evitar las funciones de lenguaje con las que (todavía) no estás completamente cómodo. ¡Mucho mejor que usar funciones que no entiendes! ;)
Steven
2
Es normal temer a lo desconocido. Para obtener más información, lea sobre varargs aquí docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Verás, que los varargs no tienen nada que temer. Los varargs son útiles. Saber cómo usar varargs le brinda la capacidad de escribir métodos como [PrintStream.format] (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)" ) :).
Desarrollador Marius Žilėnas
1
Esto no es una crítica de los varargs, pero en realidad hay alguna razón para "tener miedo" (hasta que comprenda con precisión las limitaciones de los varargs); Por eso existe la anotación @SafeVarargs. stackoverflow.com/a/14252221/1593924
Jon Coombs

Respuestas:

150

Los varargs son útiles para cualquier método que necesite tratar con un número indeterminado de objetos . Un buen ejemplo es String.format. La cadena de formato puede aceptar cualquier número de parámetros, por lo que necesita un mecanismo para pasar cualquier número de objetos.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);
Andy White
fuente
55
Un parámetro de matriz también puede recibir un número indeterminado de objetos, pero un parámetro varargs permite más flexibilidad y conveniencia en el punto de invocación. Puede escribir código para construir una matriz y pasarlo, o puede dejar que Java lo haga por usted cuando elija recibirlo en un parámetro varargs.
H2ONaCl
79

Una buena regla general sería:

"Utilice varargs para cualquier método (o constructor) que necesite una matriz de T (cualquiera que sea el tipo T) como entrada".

Eso facilitará las llamadas a estos métodos (no es necesario hacerlo) new T[]{...} ).

Puede ampliar esta regla para incluir métodos con un List<T>argumento, siempre que este argumento sea solo para entrada (es decir, el método no modifica la lista).

Además, me abstendría de usar f(Object... args)porque se desliza hacia una forma de programación con API poco claras.

En términos de ejemplos, lo he usado en DesignGridLayout , donde puedo agregar varios JComponents en una llamada:

layout.row().grid(new JLabel("Label")).add(field1, field2, field3);

En el código anterior, el método add () se define como add(JComponent... components).

¡Finalmente, la implementación de tales métodos debe tener en cuenta el hecho de que puede llamarse con un vararg vacío! Si quieres imponer al menos un argumento, entonces debes usar un truco feo como:

void f(T arg1, T... args) {...}

Considero este truco feo porque la implementación del método será menos sencilla que tener T... args en su lista de argumentos.

Espera que esto ayude a aclarar el punto sobre varargs.

jfpoilpret
fuente
1
¿Consideró agregar una verificación de precondición en su if (args.length == 0) throw new RuntimeException("foo");lugar? (Dado que la persona que llama estaba violando el contrato)
Micha Wiedenmann
24
Bueno, el objetivo de una buena API es evitar el mal uso lo antes posible, por lo que en el momento de la compilación cuando sea posible, de ahí la sugerencia de void f(T arg1, T... args)que siempre se asegura de que nunca se llame sin argumento, sin necesidad de esperar hasta el tiempo de ejecución.
jfpoilpret
Creo que la mayoría de las veces una llamada a una función de solo varargs sin argumentos en absoluto equivaldrá a no hacer nada en absoluto. El punto es que proporcionar ningún argumento a una función varargs probablemente no causará un daño considerable.
WorldSEnder
Los varargs son útiles, pero no son equivalentes al uso de una matriz. Los varargs no son reembolsables. Entonces, al igual que los genéricos, se ven afectados por el borrado de tipo que bien podría ser una restricción inaceptable dependiendo del trabajo.
Juliano
34

Utilizo varargs con frecuencia para dar salida a los registros con fines de depuración.

Casi todas las clases de mi aplicación tienen un método debugPrint ():

private void debugPrint(Object... msg) {
    for (Object item : msg) System.out.print(item);
    System.out.println();
}

Luego, dentro de los métodos de la clase, tengo llamadas como las siguientes:

debugPrint("for assignment ", hwId, ", student ", studentId, ", question ",
    serialNo, ", the grade is ", grade);

Cuando estoy satisfecho de que mi código funciona, comento el código en el método debugPrint () para que los registros no contengan demasiada información extraña y no deseada, pero puedo dejar las llamadas individuales a debugPrint () sin comentar. Más tarde, si encuentro un error, simplemente elimino el código debugPrint (), y todas mis llamadas a debugPrint () se reactivan.

Por supuesto, podría evitar fácilmente varargs y hacer lo siguiente en su lugar:

private void debugPrint(String msg) {
    System.out.println(msg);
}

debugPrint("for assignment " + hwId + ", student " + studentId + ", question "
    + serialNo + ", the grade is " + grade);

Sin embargo, en este caso, cuando comento el código debugPrint (), el servidor todavía tiene que pasar por el problema de concatenar todas las variables en cada llamada a debugPrint (), aunque no se haga nada con la cadena resultante. Sin embargo, si uso varargs, el servidor solo tiene que ponerlos en una matriz antes de darse cuenta de que no los necesita. Se ahorra mucho tiempo.

Beto
fuente
44
Puede implementar un método de depuración en la superclase para imprimir cualquier objeto usando la reflexión.
Lluis Martinez
12

Varargs se puede usar cuando no estamos seguros de la cantidad de argumentos que se pasarán en un método. Crea una matriz de parámetros de longitud no especificada en segundo plano y dicho parámetro puede tratarse como una matriz en tiempo de ejecución.

Si tenemos un método que está sobrecargado para aceptar un número diferente de parámetros, entonces, en lugar de sobrecargar el método en diferentes momentos, simplemente podemos usar el concepto varargs.

Además, cuando el tipo de los parámetros va a variar, el uso de "Object ... test" simplificará mucho el código.

Por ejemplo:

public int calculate(int...list) {
    int sum = 0;
    for (int item : list) {
        sum += item;
    }
    return sum;
}

Aquí indirectamente, una matriz de tipo int (lista) se pasa como parámetro y se trata como una matriz en el código.

Para una mejor comprensión, siga este enlace (me ayudó mucho a comprender este concepto claramente): http://www.javadb.com/using-varargs-in-java

PD: Incluso tenía miedo de usar varargs cuando no lo sabía. Pero ahora estoy acostumbrado. Como se dice: "Nos aferramos a lo conocido, tememos a lo desconocido", así que úsalo tanto como puedas y a ti también te va a gustar :)

Sarvan
fuente
44
C # Equivalente de varargs es "params". También hace lo mismo y acepta un número variable de parámetros. Vea esto para una mejor comprensión: dotnetperls.com/params
Sarvan
11

Varargs es la característica agregada en la versión 1.5 de Java.

¿Por qué usar esto?

  1. ¿Qué sucede si no conoce la cantidad de argumentos que debe pasar para un método?
  2. ¿Qué sucede si desea pasar un número ilimitado de argumentos a un método?

¿Cómo funciona esto?

Crea una matriz con los argumentos dados y pasa la matriz al método.

Ejemplo:

public class Solution {



    public static void main(String[] args) {
        add(5,7);
        add(5,7,9);
    }

    public static void add(int... s){
        System.out.println(s.length);
        int sum=0;
        for(int num:s)
            sum=sum+num;
        System.out.println("sum is "+sum );
    }

}

Salida:

2

suma es 12

3

suma es 21

Arun Prakash
fuente
6

También tengo un miedo relacionado con los varargs:

Si la persona que llama pasa una matriz explícita al método (en oposición a múltiples parámetros), recibirá una referencia compartida a esa matriz.

Si necesita almacenar esta matriz internamente, es posible que desee clonarla primero para evitar que la persona que llama pueda cambiarla más tarde.

 Object[] args = new Object[] { 1, 2, 3} ;

 varArgMethod(args);  // not varArgMethod(1,2,3);

 args[2] = "something else";  // this could have unexpected side-effects

Si bien esto no es realmente diferente de pasar cualquier tipo de objeto cuyo estado podría cambiar más tarde, ya que la matriz es generalmente (en caso de una llamada con múltiples argumentos en lugar de una matriz) una nueva creada internamente por el compilador que puede uso, este es ciertamente un comportamiento inesperado.

Thilo
fuente
1
Correcto, pero este ejemplo parece un poco exagerado. ¿Es probable que la gente use tu API de esta manera?
jfpoilpret
2
Nunca se sabe ... Y especialmente cuando el número de argumentos no está codificado en el lado de la llamada, sino que también se recopila en una lista en un bucle, pasar una matriz no es tan infrecuente.
Thilo el
55
Creo que su ejemplo de caso de uso es, en general, no improbable. Se podría argumentar que su API no debe usar la matriz de entrada de esta manera, y si lo hace, debe documentarla.
Lawrence Dol
sobrecargar el método para soportar ambos; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman
2
@ thetoolman: No creo que sea posible. Será considerado como un método duplicado.
Thilo
1

Utilizo varargs con frecuencia para constructores que pueden tomar algún tipo de objeto de filtro. Por ejemplo, una gran parte de nuestro sistema basado en Hadoop se basa en un Mapper que maneja la serialización y deserialización de elementos a JSON, y aplica una cantidad de procesadores que toman un elemento de contenido y lo modifican y lo devuelven, o devuelven un valor nulo rechazar.

Kevin Peterson
fuente
1

En Java doc de Var-Args está bastante claro el uso de var args:

http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html

sobre el uso dice:

"Entonces, ¿cuándo debería usar varargs? Como cliente, debe aprovecharlos cada vez que la API los ofrezca. Los usos importantes en las API centrales incluyen reflexión, formato de mensajes y la nueva función printf. Como diseñador de API, debe usarlos con moderación, solo cuando el beneficio es realmente convincente. En términos generales, no debe sobrecargar un método varargs, o será difícil para los programadores averiguar qué sobrecarga se llama ".

Surendra
fuente