2010-09-21 6 views
8
public static void main(String[] args) { 
    Map<String, Map<Long, List<String>>> map = getHashMap(); 
} 

static <K,V> Map<K,V> getHashMap() 
{ 
    return new HashMap<K, V>(); 
} 

Vi un código similar en google guava (como métodos de fábrica) para hacer instancias de Hashmap sin mencionar los tipos genéricos. No entiendo cómo el programa anterior infiere el genérico. Me refiero a cómo puede la función getHashMap entiende el tipo de mapa ya que no estoy pasando ningún tipo de información a la función.¿Cómo se puede inferir aquí el tipo genérico?

Respuesta

6

La función getHashMap no tiene que inferir los tipos. Es en el sitio de llamada que javac es requerido por Java Language Spec para inferir que los tipos están de acuerdo (15.12.2.7 Inferring Type Arguments Based on Actual Arguments).

Creo que el plan actual es (todavía) para que JDK7 admita el operador de diamante, por lo que este tipo de cosas también funcionará con new, aunque con un poco de sintaxis aparentemente sin sentido.

Map<String, Map<Long, List<String>>> map = new HashMap<>(); 
                 ^^diamond 
+1

¿Puede explicarme un poco? – Emil

+1

@Emil ¿Sobre qué? –

+1

@Tom: ¿Qué quiere decir con que javac inferirá el tipo? ¿Puede explicar cómo funciona esto en los pasos? – Emil

1

A nivel de código de bytes del método tendrá un descriptor que se limita a decir, hay un método con el nombre de getHashMap, que no tiene argumentos y devuelve un mapa (no hay genéricos).

Luego, cuando el compilador está analizando la línea Map<String, Map<Long, List<String>>> map = getHashMap();, y dirá, bien, tengo que tener una variable con un tipo declarado de Map<String, Map<Long, List<String>>>, pero para realmente obtener la instancia Tengo que llamar a un método. En este punto, es tarea del compilador comprobar si el tipo de devolución del método coincide con el tipo declarado de la variable a la que se asigna ese resultado. Por lo tanto, comprueba si String coincide con K, y si Map<Long, List<String>> coincide con V, lo que hacen, por lo que considera que la asignación es segura y genera el bytecode que básicamente utiliza una variable de mapa (no genéricos).

Si hubiera declarado su método:

static <K extends Number,V> Map<K,V> getHashMap() 
{ 
    return new HashMap<K, V>(); 
} 

al analizar la asignación, el compilador vería que no coincide con StringK extends Number, arrojaría un error de compilación, y no va a crear el código de bytes de esa asignación.

1
class XX 
    static <T> T foo(){ return null; } 

String s = XX.foo(); 
Integer i = XX.foo(); 

Java infiere que T es String en el primer caso, Entero en el segundo caso.

¿Qué significa "inferir"? Significa que Java adivina que en las dos afirmaciones, el programador que más quiere es T == String en el primer caso, T == Entero para el segundo caso, siendo un tipo agradable, Java toma estas suposiciones como un hecho, y los programadores no lo hacen tiene que especificar manualmente el Ts

String s = XX.<String> foo(); 
    Integer i = XX.<Integer>foo(); 

Pero, en realidad, Java dicta que el tipo de retorno T debe ser determinada por el estilo.

Me parece muy sospechoso, no estoy seguro de cuál es la razón detrás del diseño. Probablemente, cuando (si) de Java añade tipos reifiable (es decir, la clase real de T está disponible en tiempo de ejecución), el diseño tiene más sentido:

class XX 
    static <T> T foo(){ return new T(); } 

String s = XX.foo(); 
Integer i = XX.foo(); 

Todavía no me gusta el hecho de que el tipo de un método depende del contexto

Cuestiones relacionadas