GUJ Discussões   :   últimos tópicos   |   categorias   |   GUJ Respostas

Varargs - aplicação prática


#1

Pessoal,

Alguém poderia me dar um exemplo de aplicação prática de varargs?

Digo isso porque não vejo muita diferença disso:

public metodo (int... valores) { }

pra isso:

public metodo (int[] valores) { }

é uma questão de semântica da linguagem (ficar mais legível) ou há uma vantagem que eu não estou enxergando?


#2

Isso na verdade foi muito discutido antes da sua adoção na linguagem.
Como você sabe, varargs na verdade é uma abreviação de uma passagem de um parâmetro array. Por exemplo:

System.out.printf ("%d %s", 10, "Hello");

é uma abreviação de

System.out.printf ("%d %s", new Object[]{ 10, "Hello"});

Havia gente que propunha não haver varargs (porque eles acarretam algumas ambiguidades sintáticas que são difíceis de entender). Em seu lugar essas pessoas propunham que o inicializador de arrays (new Object[]{}) pudesse ser abreviado para {}. Por exemplo:

System.out.printf ("%d %s", {10, "Hello"});

Só que foram voto vencido - afinal de contas, varargs permitem que você construa algo parecido com o printf, e como você deve saber é difícil para alguém que programou em C alguma vez na vida deixar de usar o printf.


#3

entendi...

nesse caso seria algo mais "cosmético" por assim dizer né?


#4

Isso é o que o pessoal chama de "syntatic sugar" (açúcar sintático).

Eu confesso que uso bastante, já que muitas vezes eu preciso passar um array de objetos ou strings, e tenho preguiça de usar "new String[]{}" ou "new Object[]{}".

Só que varargs está mais para "syntatic sweetener" (adoçante sintático) porque ele pode ter um sabor amargo às vezes. Por exemplo, quando você faz isto:

System.out.printf ("%s", null);

o que você está fazendo?

a) Estou passando um array, cujo valor é new Object[]{null}
b) Estou passando o valor null .

Vou checar aqui na JLS o que foi definido nesse caso.


#5

eu acho que nesse caso é: Object[]{null} não?

já que essa sintaxe é um atalho pra um array...


#6

No Eclipse, eu fiz o seguinte teste:

01 class VarargsTest{
02   public static void m(String ...strings) {
03     System.out.println("" + strings);
04   }
05   
06   public static void main(String[] args) {
07     m(null);
08     m((String[])null);
09     m((String)null);
10   }
11 }

Na linha 7, o Eclipse dá o seguinte warning:

Quando você passa somente null, ele encara que vc está passando null mesmo. Se vc quer mandar um elemento String null, você deve dar um cast no null para String.
Mas assim, essas verificações são empíricas... Vai saber se isso é coisa da JLS ou se é só do Eclipse mesmo...


#7

Bom, ele dá um warning e passa "null", não "new Object[]{null}". Que legal :frowning:

class TesteVarargs {
    public void teste (Object... args) {
        if (args == null) System.out.println ("null!");
        else if (args instanceof Object[]) {
            System.out.println ("Object[] com " + args.length + " elemento(s)");
            for (Object obj : args) {
                System.out.println ("\t[" + obj + "]");
            }
        }
    }
    public static void main(String[] args) {
        TesteVarargs tv = new TesteVarargs();
/*
A seguinte linha provocou um warning, pois a chamada é ambígua.
TesteVarargs.java:22: warning: non-varargs call of varargs method with inexact ar
gument type for last parameter;
cast to java.lang.Object for a varargs call
cast to java.lang.Object[] for a non-varargs call and to suppress this warning
        tv.teste (null);
*/        
        tv.teste (null); // imprime "null!"
        tv.teste();       // imprime "Object[] com 0 elemento(s)"
        tv.teste (1);     // imprime "Object[] com 1 elemento(s)" "[1]"
        tv.teste((Object) null); // imprime "Object[] com 1 elemento(s)" "[null]"
        tv.teste(new Object[]{null}); // imprime "Object[] com 1 elemento(s)" "[null]"
        tv.teste((Object[]) null); // imprime "null!"
    }
}

#8

seguir a lógica mesmo... nada né? =:confused:

bom com certeza isso é uma questão da prova de certificação né? então niguém erra mais ela hehehehe


#9

Mantu, você que sabe ler língua de advogado, perdão, definições formais de linguagens, deve estar entendendo aqui que:

(Só para lhe ajudar, "Variable arity" = varargs)

Digamos que apareça apenas um argumento. Se ele for "assignment compatible" com o tipo T[] - no seu exemplo, T = String, pois você declarou "String... strings", então ele é passado diretamente, em vez de ser encapsulado em um new T[]{} . Portanto, você poderia usar:
m (new String[] {"a", "b", "c"}) ou m ("a", "b", "c"). Ambas as formas são equivalentes.

O problema é que a constante "null" é "assignment compatible" com qualquer tipo, se você olhar na JLS em algum outro lugar. Portanto você teria de passar null e não new String[]{null}.

Na JLS não se diz que é recomendado emitir um warning no caso do valor "null". Mas como esse caso é realmente difícil de entender, tanto o Eclipse quanto o javac emitem esse warning.


#10

Língua de advogado? Por quê?
Língua de perdão: [color=blue][b]Eita memória boa da muléstia!!![/b][/color]
Definições formais de linguagens? Quem me dera...
Aridade (Arity): A grosso modo, é a quantidade de parâmetros de um método (linguagens imperativas), de uma função(linguagens funcionais, tipo Lisp), ou de uma regra(linguagens ??????, tipo Prolog). Aprendi isso graças a [color=blue][b]uma excelente professora na facul[/b][/color]

Estou dando uma lida na parte da JSL que trata sobre como o Java deve avaliar invocações de métodos... Até agora, acho que tem algo a ver com o fato dele especificar que o método mais específico é o que deve ser escolhido para a execução... mas ainda estou lendo...


#11

Eu digo que é "linguagem de advogado" porque definições de linguagens se assemelham àquelas letras miúdas que aparecem em contratos. Só eles conseguem entender essas coisas, e olhe lá.
E isso que a JLS contém alguns exemplos, para ajudar a clarificar o que está escrito formalmente mas não está escrito muito claramente.


#12

Bom, é meio complicado juntar as pecinhas das especificações da JLS (Java Language Specification), então posso ter comido bola em algum ponto... :?
O que eu consegui "montar" foi mais ou menos isso:
.

public void foo(int i, bool b, String...strs){
   //...
}

E uma invocação deste método da seguinte forma:

foo(123, true, null);

Uma JVM que implementa corretamente (!) o que reza a JLS 15.12.2.3, não precisará executar as instruções especificadas pela JLS 15.12.2.4.
Esta afirmação se justifica nos termos da JLS 15.12.2.3, a qual diz, em termos resumidos, que a invocação "casa" com um determinado método m se este tem uma correspondência de aridade com a aridade da invocação.
Isto de fato acontece no caso acima descrito! Comparemos os argumentos da invocação com os parâmetros formais do método foo

Parâmetro	Tipo(Argumento) 	Tipo(Formal)	Casa?
1o.		int			int		Sim
2o.		boolean			boolean		Sim
3o.		null			String[]	Sim(Segundo JLS 5.2)

Esse é o motivo pelo qual as JVMs mais conhecidas não tratam a invocação acima como algo do tipo

foo(123, true, new String[]{null});

Pois a resolução desta instrução se encaixa na JLS 15.12.2.3 antes mesmo de passar por uma JLS que especifique a verificação de se o método é de aridade variável.
Se não há protestos... No further questions, your honor..


#13

Pois é, seu Mantu, é por isso que mesmo os caras que efetuam a manutenção do compilador (o Peter van der Ahé no caso do Javac, não sei quem no caso do Eclipse Compiler) se embananam um pouco para entender a JLS.
Pelo menos ambos os compiladores emitem um warning no caso de varargs, quando a especificação diz uma coisa que não é intuitiva, como é o caso de passar apenas um parâmetro "null".


#14

varargs pode ser bastante útil pra tornar mais legível algumas implementações. Por exemplo, normalmente quando uso DAO genérico + Hibernate, eu crio um método como o abaixo:

    @SuppressWarnings("unchecked")
    protected T findUniqueByCriteria(Criterion... criterion) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterion) {
            crit.add(c);
        }
        return crit.uniqueResult();
   }

Facilitando a escrita de outros métodos que usem Criteria.

	public User search(String login, String password) {
		return findUniqueByCriteria(Restrictions.eq("login", login),
					Restrictions.eq("password", password));
	}

#15

Valeu Plentz!

era exatamente esse tipo de exemplo que eu queria.

Claro que isso não desmerece em nenhum momento thingol e Mantu que participaram da thread, escovar bits nunca é demais :wink:


#16