Quando você usa varargs em Java?

192

Eu tenho medo de varargs. Não sei para que usá-los.

Além disso, parece perigoso deixar as pessoas passarem quantos argumentos quiserem.

Qual é o exemplo de um contexto que seria um bom lugar para usá-los?

Harry Quince
fonte
3
Não vejo por que seria "perigoso". Não é mais perigoso do que ter um método chamado muitas vezes com argumentos diferentes. Você tem preocupações com a pilha? Então você não deve, porque os varargs são mapeados para matrizes que são passadas por referência.
jfpoilpret
66
Só queria entrar em cena: não há nada errado em evitar os recursos de idioma com os quais você ainda não se sente totalmente confortável. Muito melhor do que usar recursos que você não entende! ;)
Steven
2
É normal temer o desconhecido. Para saber mais, leia sobre varargs aqui docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Você verá que os varargs não são nada a temer. Varargs são úteis. Saber como usar varargs permite escrever métodos como [PrintStream.format] (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)" ) :).
Desenvolvedor Marius ėilėnas
1
Isso não é uma crítica a varargs, mas na verdade há algum motivo para "ter medo" (até você entender com precisão as limitações de varargs); é por isso que existe a anotação @SafeVarargs. stackoverflow.com/a/14252221/1593924
Jon Coombs

Respostas:

150

Varargs são úteis para qualquer método que precise lidar com um número indeterminado de objetos . Um bom exemplo é String.format. A string de formato pode aceitar qualquer número de parâmetros, portanto, você precisa de um mecanismo para transmitir qualquer 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
fonte
5
Um parâmetro de matriz também pode receber um número indeterminado de objetos, mas um parâmetro varargs permite mais flexibilidade e conveniência no ponto de chamada. Você pode escrever o código para criar uma matriz e transmiti-la, ou pode permitir que o Java faça isso quando optar por recebê-lo em um parâmetro varargs.
H2ONaCl
79

Uma boa regra geral seria:

"Use varargs para qualquer método (ou construtor) que precise de uma matriz de T (qualquer que seja o tipo T) como entrada".

Isso facilitará as chamadas para esses métodos (não é necessário fazer new T[]{...}).

Você pode estender esta regra para incluir métodos com um List<T>argumento, desde que esse argumento seja apenas para entrada (ou seja, a lista não é modificada pelo método).

Além disso, eu evitaria usá-lo f(Object... args)porque ele desliza para um caminho de programação com APIs pouco claras.

Em termos de exemplos, eu o usei no DesignGridLayout , onde posso adicionar vários JComponents em uma chamada:

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

No código acima, o método add () é definido como add(JComponent... components).

Finalmente, a implementação de tais métodos deve cuidar do fato de que pode ser chamado com um vararg vazio! Se você deseja impor pelo menos um argumento, use um truque feio, como:

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

Considero esse truque feio porque a implementação do método será menos direta do que ter apenas T... argsem sua lista de argumentos.

Espero que isso ajude a esclarecer o ponto sobre as variáveis.

jfpoilpret
fonte
1
Você considerou adicionar uma verificação de pré-condição if (args.length == 0) throw new RuntimeException("foo");? (Desde o chamador estava violando o contrato)
Micha Wiedenmann
24
Bem, o objetivo de uma boa API é impedir o uso indevido o mais cedo possível, portanto, no momento da compilação, quando possível, a sugestão para void f(T arg1, T... args)isso sempre garante que nunca seja chamada sem argumento, sem a necessidade de esperar até o tempo de execução.
Jfpoilpret
Eu acho que na maioria das vezes uma chamada para uma função somente varargs sem argumentos será o suficiente para também não fazer nada. A questão é que não fornecer argumentos para uma função varargs provavelmente não causará danos consideráveis.
WorldSEnder
Varargs são úteis, mas não são equivalentes ao uso de uma matriz. Varargs não são reificáveis. Assim, como os genéricos, eles são afetados pelo apagamento de tipo, que pode muito bem ser uma restrição inaceitável, dependendo do trabalho.
Julian
34

Uso varargs frequentemente para enviar para os logs para fins de depuração.

Praticamente todas as classes no meu aplicativo têm um método debugPrint ():

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

Então, dentro dos métodos da classe, tenho chamadas como as seguintes:

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

Quando estou convencido de que meu código está funcionando, comento o código no método debugPrint () para que os logs não contenham muitas informações estranhas e indesejadas, mas posso deixar as chamadas individuais para debugPrint () descomentadas. Mais tarde, se eu encontrar um bug, apenas descomente o código debugPrint () e todas as minhas chamadas para debugPrint () serão reativadas.

Claro, eu poderia facilmente evitar varargs e fazer o seguinte:

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

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

No entanto, nesse caso, quando eu comento o código debugPrint (), o servidor ainda precisa enfrentar o problema de concatenar todas as variáveis ​​em todas as chamadas para debugPrint (), mesmo que nada seja feito com a sequência resultante. Se eu usar varargs, no entanto, o servidor só precisará colocá-los em uma matriz antes de perceber que não precisa deles. Muito tempo é economizado.

prumo
fonte
4
Você pode implementar um método de depuração na superclasse para imprimir qualquer objeto usando reflexão.
Lluis Martinez
12

Varargs pode ser usado quando não temos certeza sobre o número de argumentos a serem passados ​​em um método. Ele cria uma matriz de parâmetros de comprimento não especificado em segundo plano e esse parâmetro pode ser tratado como uma matriz em tempo de execução.

Se temos um método que está sobrecarregado para aceitar um número diferente de parâmetros, em vez de sobrecarregar o método em tempos diferentes, podemos simplesmente usar o conceito varargs.

Além disso, quando o tipo de parâmetro variar, o uso de "Objeto ... teste" simplificará muito o código.

Por exemplo:

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

Aqui, indiretamente, uma matriz do tipo int (lista) é passada como parâmetro e é tratada como uma matriz no código.

Para um melhor entendimento, siga este link (me ajudou muito a entender claramente esse conceito): http://www.javadb.com/using-varargs-in-java

PS: Até eu tinha medo de usar varargs quando não sabia disso. Mas agora estou acostumado. Como é dito: "Nos apegamos ao conhecido, com medo do desconhecido", então use-o o máximo que puder e você também começará a gostar :)

Sarvan
fonte
4
C # Equivalente a varargs é "params". Ele também faz a mesma coisa e aceita um número variável de parâmetros. Veja isso para uma melhor compreensão: dotnetperls.com/params
Sarvan
11

Varargs é o recurso adicionado na versão 1.5 do java.

Por que usar isso?

  1. E se você não souber o número de argumentos a serem transmitidos para um método?
  2. E se você quiser passar um número ilimitado de argumentos para um método?

Como isso funciona?

Ele cria uma matriz com os argumentos fornecidos e passa a matriz para o método

Exemplo:

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 );
    }

}

Resultado :

2

soma é 12

3

soma é 21

Arun Prakash
fonte
6

Também tenho um medo relacionado a varargs:

Se o chamador passar em uma matriz explícita para o método (em oposição a vários parâmetros), você receberá uma referência compartilhada para essa matriz.

Se você precisar armazenar essa matriz internamente, convém cloná-la primeiro para evitar que o chamador possa alterá-la mais 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

Embora isso não seja realmente diferente de transmitir qualquer tipo de objeto cujo estado possa ser alterado posteriormente, uma vez que a matriz é geralmente (no caso de uma chamada com vários argumentos em vez de uma matriz) uma nova criada pelo compilador internamente que você pode com segurança uso, esse certamente é um comportamento inesperado.

Thilo
fonte
1
Correto, mas este exemplo parece um pouco exagerado. É provável que as pessoas usem sua API dessa maneira?
jfpoilpret
2
Você nunca sabe ... E, especialmente, quando o número de argumentos não é codificado no lado da chamada, mas também é colecionado digamos em uma lista em um loop, passar uma matriz não é tão incomum.
Thilo
5
Eu acho que o seu exemplo de caso de uso não é improvável. Alguém pode argumentar que sua API não deve usar a matriz de entrada dessa maneira e, se o fizer, deve documentá-la.
Lawrence Dol
sobrecarregar o método para suportar os dois; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman
2
@ thetoolman: Eu não acho que isso é possível. Será considerado como um método duplicado.
Thilo #
1

Uso varargs frequentemente para construtores que podem receber algum tipo de objeto de filtro. Por exemplo, grande parte do nosso sistema baseado no Hadoop é baseada em um Mapper que lida com serialização e desserialização de itens no JSON e aplica vários processadores que cada um pega um item de conteúdo e o modifica e devolve, ou retorna nulo rejeitar.

Kevin Peterson
fonte
1

No Java doc do Var-Args, é bastante claro o uso de var args:

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

sobre o uso, diz:

"Então, quando você deve usar varargs? Como cliente, você deve tirar proveito deles sempre que a API os oferecer. Os usos importantes nas principais APIs incluem reflexão, formatação de mensagens e o novo recurso de impressão. Como designer de API, você deve usá-los moderadamente, apenas quando o benefício é realmente atraente. De um modo geral, você não deve sobrecarregar um método varargs, ou será difícil para os programadores descobrir qual sobrecarga é chamada ".

Surendra
fonte