2011-09-28 21 views
7

Esto es una continuación de mi pregunta anterior pero dado que el hilo anterior fue largo, decidí comenzar otro hilo relacionado con el mismo tema.Java Generics: pregunta sobre la captura de tipo y la inferencia generada usando métodos genéricos

public class GenericMethodInference { 

static <T> void test1(T t1, T t2) {} 
static <T> void test3(T t1, List <T> t2) {} 
static <T> void test4(List <T> t1, List <T> t2) {} 

public static void main(String [] args) { 

    List <Object> c = new LinkedList<Object>(); 
    List <? extends Object> d = new ArrayList<Integer>(); 
    List e = new ArrayList<Integer>(); 

    test1("Hello", new Integer(1)); // ok clause (1) 
    GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2) 
    test3("Hello", c); // ok clause (3) 
    test4(d,d) // clause (4) Error due to different type capture generated 

} 

Nota: Si mueve el cursor sobre cada cláusula, verá la inferencia que se genera y se muestra en Eclipse:

a. La cláusula (1) producirá <? extiende Object> test1 <? extiende Objeto,? extiende Object>
b. La cláusula (2) producirá exactamente lo que se define en el parámetro de tipo real
c. La cláusula (3) producirá < Objeto> test3 < Objeto, Lista de objetos < >>

Preguntas:

  1. Por qué la cláusula (1) no produjo < Objeto>? Dado que < Objeto> funciona como se muestra en la cláusula (2), ¿por qué <? extiende Objeto> ser producto en su lugar?
  2. por qué la cláusula (3) produce < Objeto> en lugar de <? extiende Objeto>?
  3. Como la cláusula (4) usa la misma variable, ¿por qué se generaron 2 tipos diferentes de captura aunque el parámetro utilizado sea de la misma variable d?
+0

"Si mueve el cursor sobre cada cláusula" - ¿cuál IDE por favor? (actualización: gracias por esa edición) –

+0

Eclipse, dice;) –

+0

¿Qué es la cláusula 4? –

Respuesta

4

Por qué la cláusula (1) no produjo < Objeto>? Dado que < Objeto> funciona como se muestra en la cláusula (2), ¿por qué <? extiende Objeto> ser producto en su lugar?

Esta es la mejor pregunta de las tres. Mi opinión es que el compilador/Eclipse no quiere suponer que Object es necesariamente el tipo T que se infiere entre String y Integer, por lo que se juega de forma segura. Como señaló @bringer128, String y Integer también implementan Serializable y Comparable, por lo que estos tipos también son candidatos para el tipo inferido del método.

Vale la pena señalar que el siguiente código da el error del compilador "inicio ilegal de tipo":

GenericMethodInference.<? extends Object>test1("Hello", new Integer(1)); 

Esto se debe a que es válido para especificar un comodín como parámetro de tipo de un método. Por lo tanto, el hecho de que vea que en la información sobre herramientas tiene que ver con la sutileza de las funciones del compilador/Eclipse para informar esta información, solo ha determinado que T está dentro de sus límites, no lo que es.

Recuerde que la implementación de genéricos de Java es únicamente para la comodidad/cordura de los programadores. Una vez compilado en bytecode, type erasure se habrá deshecho de cualquier noción de T. Por lo tanto, en su verificación, el compilador solo necesita asegurarse de que se pueda inferir aT válido, pero no necesariamente de qué se trata.


razón por la cláusula (3) producen < objeto> en lugar de <? extiende Objeto>?

Porque en este caso, el hecho de que un List<Object> se pasa en donde se espera una List<T> le dice al compilador que T es exactamente Object.


Desde la cláusula (4) utiliza la misma variable, por qué 2 de captura de tipo diferente generado eventhough el parámetro utilizado es del mismo d variable?

no es seguro para el compilador asumir que d en realidad se refiere al mismo objeto, incluso entre los parámetros de evaluación. Por ejemplo:

test4(d,(d = new ArrayList<String>())); 

En este caso, un List<Integer> sería pasado en el primer parámetro, y un List<String> en el segundo - tanto desde d. Dado que este escenario es posible, es más fácil para el compilador ir a lo seguro.

+1

FYI Para la cláusula 1 tienen interfaces padre comunes Serializable y Comparable. El compilador siempre tratará de inferir la interfaz más específica que pueda encontrar y no le gusta resolverlo si puede ayudarlo. – Bringer128

+0

@ Bringer128 - Gran punto, lo agregaré a mi respuesta. –

+1

Ah, y pruebo esto en IntelliJ redefiniendo el método para 'static List test1 (T t1, T t2) {return null;}' y luego usando el IDE para generar una variable (Ctrl + Alt + v) basada en el valor de retorno del método. Supongo que Netbeans puede hacer algo similar. – Bringer128

2

El caso test1() es bastante siniestro. ver JLS3 15.12.2.7.

No se supone que sepamos los detalles de la inferencia de tipo: en la mayoría de los casos, la intuición coincide con el algoritmo. Por desgracia, ese no es siempre el caso, como en el ejemplo aparentemente trivial test1().

Las limitaciones que tenemos es T :> String y T :> Integer (":>" significa supertipo)

Esto lleva a T=lub(String,Integer), lub significa "extremo superior".

Desde String <: Comparable<String> y Integer <: Comparable<Integer>, esto conduce a lci({Comparable<String>, Comparable<Integer>}), que produce Comparable<? extends lub(String,Integer)>, es decir Compable<? extends T>

Al final, tenemos T = Serializable & Compable<? extends T>, una definición referenciada auto! Spec lo llama "tipo infinito":

Es posible que el proceso anterior produzca un tipo infinito. Esto es permisible, y los compiladores de Java deben reconocer tales situaciones y representarlas apropiadamente usando estructuras de datos cíclicos.

Vamos a ver cómo javac lo representa: (javac 7)

static <T> T test1(T t1, T t2) {} 

public static void main(String[] args) 
{ 
    Void x = test1("Hello", new Integer(1)); 
} 

error: incompatible types 
required: Void 
found: INT#1 
where INT#1,INT#2 are intersection types: 
INT#1 extends Object,Serializable,Comparable<? extends INT#2> 
INT#2 extends Object,Serializable,Comparable<?> 

eso no parece derecho; no es realmente recursivo; parece que javac detecta la recursión en lub() y se da por vencido, lo que resulta en un tipo menos específico Comparable<?>

Cuestiones relacionadas