Varargs: Overload & Override

Galera, estou estudando para fazer a certificação do Java 5 e encontrei o seguinte exemplo:

[code]
public class OneSuperclass {
public int doIt(String str, Integer… data) throws java.io.EOFException,
java.io.FileNotFoundException { // (1)
String signature = “(String, Integer[])”;
out.println(str + " => " + signature);
return 1;
}

public void doIt(String str, Number... data) { // (2)
	String signature = "(String, Number[])";
	out.println(str + " => " + signature);
}

}[/code]

import static java.lang.System.out;

public class OneSubclass extends OneSuperclass {

	// public int doIt(String str, Integer[] data) 	//  Overridden (a)
	public int doIt(String str, Integer... data) // Overridden (b)
			throws java.io.FileNotFoundException {
		String signature = "(String, Integer[])";
		out.println("Overridden: " + str + " => " + signature);
		return 0;
	}

	public void doIt(String str, Object... data) { // Overloading
		String signature = "(String, Object[])";
		out.println(str + " => " + signature);
	}

	public static void main(String[] args) throws Exception {
		OneSubclass ref = new OneSubclass();
		ref.doIt("1. (String)");
		ref.doIt("2. (String, int)", 10);
		ref.doIt("3. (String, Integer)", new Integer(10));
		ref.doIt("4. (String, int, byte)", 10, (byte) 20);
		ref.doIt("5. (String, int, int)", 10, 20);
		ref.doIt("6. (String, int, long)", 10, 20L);
		ref.doIt("7. (String, int, int, int)", 10, 20, 30);
		ref.doIt("8. (String, int, double)", 10, 20.0);
		ref.doIt("9. (String, int, String)", 10, "what?");
		ref.doIt("10.(String, boolean)", false);
	}
}					

Gerando a seguinte saída:

Overridden: 1. (String) => (String, Integer[]) Overridden: 2. (String, int) => (String, Integer[]) Overridden: 3. (String, Integer) => (String, Integer[]) 4. (String, int, byte) => (String, Number[]) Overridden: 5. (String, int, int) => (String, Integer[]) 6. (String, int, long) => (String, Number[]) Overridden: 7. (String, int, int, int) => (String, Integer[]) 8. (String, int, double) => (String, Number[]) 9. (String, int, String) => (String, Object[]) 10.(String, boolean) => (String, Object[])

Consegui entender todas as saídas, exceto 1.

Alguém tem alguma explicação racional sobre a primeira saída!? Se eu encontrasse uma questão desta na prova tinha errado, pois marcaria Compilation Error :smiley: .

Interessante. Só para lembrar, não passar parâmetro quer dizer a mesma coisa que passar um array de comprimento zero.

Porque é que ele escolheu chamar a versão que usa Integer… em vez da versão que usa Object… quando não há parâmetros é que é eu não entendi ainda. Vou fazer um pequeno teste e já lhe respondo.

Credo, tive de ler o Java Language Specification versão 3.0 e achar a explicação na página 447 e 448. Parece legislação tributária, é cheio de fórmulas.

15.12.2.5 Choosing the Most Specific Method

If more than one member method is both accessible and applicable to a method
invocation, it is necessary to choose one to provide the descriptor for the run-time
method dispatch. The Java programming language uses the rule that the most specific
method is chosen.
The informal intuition is that one method is more specific than another if any
invocation handled by the first method could be passed on to the other one without
a compile-time type error.

Basicamente ele faz o seguinte:

  • Há dois métodos que se qualificam para o caso 1, que têm o parâmetro Integer… e o parâmetro Object…

EDIT - Há três - Integer, Number e Object; depois é que eu vi com mais atenção!

Como não há parâmetro nenhum, então deve passar um parâmetro que é um array de “sei lá o quê” de tamanho 0. Há dois candidatos (Integer[] e Object[]), e o mais específico é Integer[], já que todo Integer[] é um Object[], mas nem todo Object[] é um Integer[].

Experimentei modificar o programa acrescentando o seguinte método:

 	public int doIt(String str, Long... data) // Overridden (b)
 			throws java.io.FileNotFoundException {
 		String signature = "(String, Long[])";
 		out.println("Overridden: " + str + " => " + signature);
 		return 0;
 	}

e então deu o seguinte erro de compilação:

TestVarargs.java:40: reference to doIt is ambiguous, both method doIt(java.lang.
String,java.lang.Integer...) in OneSuperclass and method doIt(java.lang.String,j
ava.lang.Long...) in OneSubclass match
                ref.doIt("1. (String)");
                   ^
1 error

Basicamente isso prova que está sendo seguida a definição das páginas 447 e 448 da JLS, pois aí temos três métodos que se qualificam para o caso 1, mas os métodos com Integer… e com Long… são igualmente mais específicos que o método com Object…, dando o erro de compilação.

EDIT - veja que o método com Number… é menos específico que o método com Integer… ou Long… mas mais específico que o método com Object…

nossa, q coisa mais maluca.
mandaram bem! (na duvida e na resposta excelente!)

Pois é, nunca pensei que até varargs (que no JLS são chamados de “variable arity methods”) tivessem sutilezas. (Pensava que o compilador era preguiçoso e pegava o primeiro método que atendesse aos requisitos).

Acho que isso não deve cair na prova - é muito sutil para perguntar para um pobre programador Java - , só perguntar pro Paulo Silveira que fez a prova do Java 5.

Muito obrigado thingol pela resposta!

Esse Tiger é cheio de surpresas mesmo. Vou continuar estudando e ver se encontro outras coisas “sinistras” :smiley:

Valeu mesmo!