2009-08-18 9 views
16

Java a menudo puede inferir genéricos basados ​​en los argumentos (e incluso en el tipo de devolución, en contraste con, por ejemplo, C#).Genéricos de comodines inferidos en el tipo de retorno

Caso en cuestión: Tengo una clase genérica Pair<T1, T2> que acaba almacena un par de valores y puede ser utilizado de la siguiente manera:

Pair<String, String> pair = Pair.of("Hello", "World"); 

of El método se parece a esto:

public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
    return new Pair<T1, T2>(first, second); 
} 

Muy bonito. Sin embargo, esto ya no trabaja para el siguiente caso de uso, lo que requiere comodines: (. Note la conversión explícita para hacer List.class el tipo correcto)

Pair<Class<?>, String> pair = Pair.of((Class<?>) List.class, "hello"); 

El código de error con el siguiente error (siempre por Eclipse):

desajuste Tipo: no se puede convertir de TestClass.Pair<Class<capture#1-of ?>,String> a TestClass.Pair<Class<?>,String>

Sin embargo, una llamada explícita al constructor sigue funcionando como se esperaba:

Pair<Class<?>, String> pair = 
    new Pair<Class<?>, String>((Class<?>) List.class, "hello"); 

Puede alguien explicar este comportamiento? ¿Es por diseño? ¿Es buscado? ¿Estoy haciendo algo mal o tropecé con un error en el diseño/error en el compilador?

conjetura salvaje: la “captura # 1 de?” De alguna manera parece implicar que el comodín es rellenado por el compilador sobre la marcha, por lo que el tipo de un Class<List>, y no así la conversión (de Pair<Class<?>, String> a Pair<Class<List>, String>) . ¿Es esto correcto? ¿Hay alguna manera de evitar esto?


Para completarlo, aquí es una versión simplificada de la clase Pair:

public final class Pair<T1, T2> { 
    public final T1 first; 
    public final T2 second; 

    public Pair(T1 first, T2 second) { 
     this.first = first; 
     this.second = second; 
    } 

    public static <T1, T2> Pair<T1, T2> of(T1 first, T2 second) { 
     return new Pair<T1, T2>(first, second); 
    } 
} 
+0

Parece que el convertidor ve la firma de "de" cuando devuelve un par ,? extiende clase > tipo. Para las clases finales parece lo suficientemente inteligente como para reducir la parte de las extensiones, es por eso que no se queja en el String. – Zed

+0

Hmmm, interesante. Gracias por vincularme aquí. – jjnguy

+0

Ahora funciona en java8. El tipo de destino también se consulta para inferenarse. – ZhongYu

Respuesta

13

La razón por la que funciona el constructor es que estás especificar explícitamente los parámetros de tipo. El método estático también trabajará si hacer eso:

Pair<Class<?>, String> pair = Pair.<Class<?>, String>of(List.class, "hello"); 

Por supuesto, toda la razón por la que tienen un método estático en el primer lugar es probablemente sólo para obtener la inferencia de tipos (que no funciona con los constructores en todas).

El problema aquí (como sugeriste) es que el compilador está realizando capture conversion. Creo que esto es como resultado de [§15.12.2.6 of the JLS]:

  • El tipo de resultado del método elegido se determina de la siguiente manera:
    • Si se declara el método que se invoca con un tipo de retorno de vacío, entonces el resultado es nulo.
    • lo contrario, si era necesario conversión sin marcar para el método sea aplicable a continuación, el tipo de resultado es el borrado (§4.6) de del método declarado tipo de retorno.
    • De lo contrario, si se invoca el método es genérico, a continuación, para 1in, deja Fi sea los parámetros de tipo formal de el método, Sea Ai el tipo real argumentos inferidos para la invocación del método , y sea R la declaró tipo de devolución del método siendo invocado. El tipo de resultado se obtiene aplicando la conversión de captura (§5.1.10) a R [F1: = A1, ..., Fn: = An].
    • De lo contrario, el tipo de resultado se obtiene aplicando la conversión de captura (§5.1.10) al tipo dado en la declaración del método.

Si realmente desea la inferencia, una posible solución es hacer algo como esto:

Pair<? extends Class<?>, String> pair = Pair.of(List.class, "hello"); 

La variable pair tendrá un tipo más amplio, y que sí significa un poco escriba más en el nombre del tipo de la variable, pero al menos ya no necesita lanzar la llamada al método.

+0

Muchas gracias. Todavía estoy considerando si la solución no hace que el núcleo sea aún más confuso. Por el momento, lo dejaré como está. –

Cuestiones relacionadas