2009-10-03 17 views
5

Tengo una interfaz A, que clase B implementa.Java Generics Curiosity

El siguiente método genérico funciona

public static <T, U extends T> List<T> listFactory(Collection<U> source) { 
    return new ArrayList<T>(source); 
} 

pero

public static <T> List<T> listFactory(Collection<? extends T> source) { 
    return new ArrayList<T>(source); 
} 

no (error de compilación, de coincidencia de tipos), cuando estoy dirigiendo la salida en

List<A> tester = listFactory(B.defaultCollectionFactory(3)); 

defaultCollectionFactory(int count) estáticamente proporciona una colección de B s, con un esquema de etiquetado predeterminado.

¿Alguna idea de por qué es así? Parece que el U genérico y el comodín están haciendo lo mismo.

+1

¿cómo "no funciona"? ¿Cuál es el mensaje de error? – newacct

+1

Parece que tiene dos respuestas correctas a continuación para saber qué es lo que está causando el problema, pero yo no tomaría su consejo literalmente. Quédese con la versión que no sea comodín, para que la persona que llama del método no tenga que solucionar esta limitación de comodines. –

+0

@newacct: error de compilación, tipo no coincide – Carl

Respuesta

2

En la primera construcción, especifica que está devolviendo List de la interfaz del elemento que se pasó. Usted especifica la relación entre el objeto pasado en Object y el tipo de objeto devuelto en la dirección U extends T. En este caso, el compilador puede asociar A y B con T y U respectivamente.

En el segundo, no hay tal diferenciación, por lo que el compilador supone que T se refiere a y escribirá el valor de retorno como List<B>. Luego cae en la trampa donde, aunque B es una instancia de A, List<B> no es una instancia de List<A>. El compilador se quejará:

coinciden los tipos: no se puede convertir de Lista <B> a la lista <Un>

se encuentra que, con el primer constructo, usted tiene la libertad de especificar un List de cualquier interfaz implementa B o cualquier superclase en la jerarquía B (List<Object>, por ejemplo), y el compilador no se quejará.

+0

Por lo tanto, la precedencia (tipo de) para la inferencia de tipo proviene de los argumentos primero, y luego del tipo de retorno para este tipo de genéricos. – Carl

+0

elegido en base a una buena explicación (que también ofrece erickson), además de evitar aconsejar a la Clase. estructura, que creo que es feo. – Carl

3

El compilador está infiriendo un parámetro de tipo diferente para el método listFactory de lo esperado. Se infiere que T es tipo B, por lo que la firma es efectivamente List<B> listFactory(Collection<? extends B> source). Especifique el parámetro de tipo A al ser explícito en la invocación al método:

List<A> tester = Test.<A> listFactory(B.defaultCollectionFactory(3)); 
+0

@erikson: Esta llamada estática en un tipo parametrizado realmente funciona, no tiene sentido para mí, ¿puedes desglosarlo con un ejemplo y una explicación teórica, gracias? –

+0

Cualquier método estático puede (realmente, debería) ser calificado por la clase a la que pertenece. El OP no especificó qué es; Usé "Prueba". Así que eso le daría una llamada normal al método estático: 'Test.listFactory (...)'. Luego, los argumentos de tipo para cualquier método parametrizado se pueden especificar explícitamente, agregándolos antes del nombre del método. Esa es la ''. Póngalos juntos, y obtendrá mi respuesta. – erickson