2011-12-26 5 views
13

El siguiente código¿Por qué un molde genérico de una lista <? extiende Set ..> a la Lista <Set..> ¿Tiene éxito en Sun JDK 6 pero no compila en Oracle JDK 7?

class GenericCompilationFailureDemo { 
    List<? extends GenericCompilationFailureDemo> newList() { 
     return new ArrayList<GenericCompilationFailureDemo>(); 
    }; 

    void useList() { 
     List<GenericCompilationFailureDemo> list = 
      (List<GenericCompilationFailureDemo>) newList(); 
    } 

    List<? extends Set<GenericCompilationFailureDemo>> newListOfSpecificSets() { 
     return new ArrayList<Set<GenericCompilationFailureDemo>>(); 
    }; 

    void useListOfSpecificSets() { 
     List<Set<GenericCompilationFailureDemo>> listOfSpecificSets = 
      (List<Set<GenericCompilationFailureDemo>>) newListOfSpecificSets(); 
    } 

    List<? extends Set<? extends GenericCompilationFailureDemo>> newListOfSets() { 
     return new ArrayList<Set<? extends GenericCompilationFailureDemo>>(); 
    }; 

    void useListOfSet() { 
     List<Set<? extends GenericCompilationFailureDemo>> listOfSets = 
      (List<Set<? extends GenericCompilationFailureDemo>>) newListOfSets(); 
    } 
} 

compila bajo Sun JDK 1.6.0_20 (64 bits en Windows Vista, pero no creo que hay alguna diferencia), pero hace que el siguiente fallo de compilación bajo Oracle JDK 1.7.0_01 (misma plataforma):

[ERROR] src\main\java\GenericCompilationFailureDemo.java:[56,78] error: inconvertible types 

Tenga en cuenta que el primero de dos "se extiende a-tipo específico" echa en useList y useListOfSpecificSets ambos todavía tener éxito bajo 1.7.0_01, por lo que parece que es algo que ver con el "doble genérico se extiende".

¿Alguna idea de lo que podría haber cambiado entre 6 y 7, y si el comportamiento observado es según la especificación o un error?

editado en respuesta al comentario de Sanjay:

@Sanjay: Aha, interesante! Aquí la salida de java -version:

java version "1.7.0_01" 
Java(TM) SE Runtime Environment (build 1.7.0_01-b08) 
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode) 

Y aquí el resultado de javac GenericCompilationFailureDemo.java (mismo código que el anterior con las declaraciones de importación de lista, ArrayList y Set):

GenericCompilationFailureDemo.java:30: error: inconvertible types 
      (List<Set<? extends GenericCompilationFailureDemo>>) newListOfSets() 
; 
                      ^
    required: List<Set<? extends GenericCompilationFailureDemo>> 
    found: List<CAP#1> 
    where CAP#1 is a fresh type-variable: 
    CAP#1 extends Set<? extends GenericCompilationFailureDemo> from capture of ? 
extends Set<? extends GenericCompilationFailureDemo> 
Note: GenericCompilationFailureDemo.java uses unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 
1 error 
+1

Por lo que vale la pena, este código se compila en mi JDK 7. ¿Se puede publicar toda la salida de 'java -version' comando junto con la "acumulación " ¿cuerda? –

+0

por favor pegue los errores exactos de compilación + lo que sugirió sanjay. * Creo que los genéricos se supone que son un poco más simples en java 7 * en algunos casos ... tal vez encontraste un caso de esquina donde una sintaxis JDK 6 más antigua y compleja rompe el nuevo compilador. – jayunit100

+0

@Sanjay: aha, ¡interesante! Consulte la pregunta editada para obtener respuestas a sus comentarios –

Respuesta

7

Esto es al parecer un error javac7. Cabe permitido por las reglas de conversión de colada [1]

Una de las reglas permite una conversión de referencia estrechamiento ... seguido de una conversión sin control

casting List<A> => List<B> está permitido por esta regla

List<A> => List // narrowing reference conversion 
List => List<B> // unchecked conversion 

Sin embargo, esa no es toda la historia; la especificación tiene reglas adicionales para prohibir la conversión como List<String>=>List<Integer>, ya que son parametrizables claramente distintos tipos. No hay ningún objeto que pertenezca a los dos tipos al mismo tiempo, por lo que el compilador cree que es mejor no permitir este aparente error de programación. (Puede omitirlo explícitamente List<String>=>List=>List<Integer>)

La última regla no se aplica aquí; por lo que parece un error javac7.

Por qué no se aplica la última regla: así que estamos lanzando List<? extends A> a List<A>. Aquí la conversión de captura se aplica a List<? extends A> [2] por lo que estamos realmente lanzando List<T> a List<A>, donde T es una nueva variable de tipo con límite superior A.

La pregunta es si List<T> y List<A> son tipos parametrizados demostrablemente distintas. Según entiendo, es falso (tiene que ser falso para compilar los dos primeros ejemplos). Como T es una variable de tipo, puede tomar un valor para hacer List<T> y List<A> el mismo tipo parametrizado (es decir, cuando T=A). Este razonamiento debería funcionar para cualquier tipo A.

[1] http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.5

[2] http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#341306

+0

_Puedes eludirlo explícitamente List => List => List ) _ eso, de hecho, pero "hackear genéricos" no era algo que realmente quisiera comprometer ;-) ¡Gracias por la explicación! ¿Hay alguna persona dentro de Oracle con la que sugiera contactar (por ejemplo, la lista compilador-dev, o algo así?) Para ver si vale la pena archivar un error para esto? –

+0

la lista del compilador-dev debe ser buena – irreputable

+1

Acaba de publicarse en el compilador-dev. Es curioso ver qué tipo de respuestas obtendremos ;-) –

Cuestiones relacionadas