metodos - Invocar métodos genéricos de Java
objeto t en java (3)
El tipo heredado compartido de String
y Long
es Object
.
Cuando ejecuta esta función como Util.<String>compare(
el compilador espera encontrar dos entradas de cadena y da un error cuando no lo hace. Sin embargo, ejecutarlo sin <String>
resulta en el uso del tipo heredado compartido más cercano - en este caso, Object
.
Por lo tanto, cuando la compare
acepta t1
y t2
, se han convertido en Object
, y el código funciona bien.
Para obtener el tipo real en tiempo de ejecución, utilice la misma técnica que usaría con cualquier otro objeto: el getClass()
que se hereda de la clase Object
.
Estoy estudiando la característica genérica de Java y no estoy seguro de cómo explicar la tercera línea en el siguiente método main
:
public class Example4 {
public static void main(final String[] args) {
System.out.println(Util.<String>compare("a", "b"));
System.out.println(Util.<String>compare(new String(""), new Long(1)));
System.out.println(Util.compare(new String(""), new Long(1)));
}
}
class Util {
public static <T> boolean compare(T t1, T t2) {
return t1.equals(t2);
}
}
La primera línea compila, ejecuta y devuelve (como se esperaba) false
.
La segunda línea no se compila como se esperaba, porque estoy mezclando explícitamente String
y Long
.
La tercera línea compila, ejecuta y devuelve falso, pero no estoy seguro de entender cómo funciona: ¿el compilador / JVM crea una instancia del tipo de parámetro T
como Object
? (Además, ¿habría una manera de obtener este tipo declarado de T
tiempo de ejecución?)
Gracias.
La respuesta parece ir más allá de las respuestas de @Telthien y @newacct ''. Tenía curiosidad por "ver" por mí mismo la diferencia entre:
System.out.println(Util.<String>compare("a", "b"));
con tipificación explícita, y:
System.out.println(Util.compare(new String(""), new Long(1)));
con la tipificación implícita.
Realicé varios experimentos, utilizando variaciones en estas dos líneas anteriores. Estos experimentos muestran que, aparte de usar el truco de clase anónimo / local , el compilador comprueba los tipos durante la compilación, pero los códigos de bytes generados solo se refieren a Object
, incluso en el caso de la primera línea.
El siguiente fragmento de código muestra que las predicciones de tipo se pueden realizar de forma segura hasta Object
incluso en el caso del argumento de tipo explícito <String>
.
public final class Example44 {
public static void main(final String[] args) {
System.out.println(new Util44<String>().compare("a", "b"));
System.out.println(new Util44().compare(new String(""), new Long(1)));
}
}
final class Util44<T> {
private T aT;
public boolean compare(T t1, T t2) {
System.out.println(this.aT);
// I was expecting the second and third assignments to fail
// with the first invocation because T is explicitly a String
// and then to work with the second invocation because I use
// a raw type and the compiler must infer a common type for T.
// Actually, all these assignments succeed with both invocation.
this.aT = (T) new String("z");
this.aT = (T) new Long(0);
this.aT = (T) new Object();
return t1.equals(t2);
}
}
Los bytecodes del método main
se ven así:
// Method descriptor #15 ([Ljava/lang/String;)V
// Stack: 7, Locals: 1
public static void main(java.lang.String[] args);
0 getstatic java.lang.System.out : java.io.PrintStream [16]
3 new ca.polymtl.ptidej.generics.java.Util44 [22]
6 dup
7 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24]
10 ldc <String "a"> [25]
12 ldc <String "b"> [27]
14 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29]
17 invokevirtual java.io.PrintStream.println(boolean) : void [33]
20 getstatic java.lang.System.out : java.io.PrintStream [16]
23 new ca.polymtl.ptidej.generics.java.Util44 [22]
26 dup
27 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24]
30 new java.lang.String [39]
33 dup
34 ldc <String ""> [41]
36 invokespecial java.lang.String(java.lang.String) [43]
39 new java.lang.Long [46]
42 dup
43 lconst_1
44 invokespecial java.lang.Long(long) [48]
47 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29]
50 invokevirtual java.io.PrintStream.println(boolean) : void [33]
53 return
Line numbers:
[pc: 0, line: 24]
[pc: 20, line: 25]
[pc: 53, line: 26]
Local variable table:
[pc: 0, pc: 54] local: args index: 0 type: java.lang.String[]
Realmente tiene sentido que todas las llamadas sean siempre a métodos con Object
como tipos de parámetros formales, como se explica en otra pregunta / respuesta . Para suponer, el compilador siempre usa Object
para los bytecodes generados, no importa si hay un argumento de tipo explícito (primera línea) o un argumento de tipo implícito pero que los objetos podrían tener una superclase común diferente de Object
.
Sí, Object
es una opción para T
que le permitirá compilar. Conceptualmente, el compilador infiere un tipo para T
Lo que infiere particularmente no importa, siempre que pueda inferir que algún tipo funcionará para T
, luego compila. No importa cuál sea el tipo inferido, ya que no tiene efecto en el código compilado.