2011-03-25 15 views
7

Obtuve un extraño error de compilación al usar genéricos dentro de un bucle for-each en Java. ¿Es esto un error del compilador de Java, o realmente me falta algo aquí?¿Por qué el compilador de Java se queja de utilizar foreach con un tipo sin formato?

Aquí es toda mi clase:

public class Generics<T extends Object> { 
    public Generics(T myObject){ 
    // I didn't really need myObject 
    } 

    public List<String> getList(){ 
    List<String> list = new ArrayList<String>(); 
    list.add("w00t StackOverflow"); 
    return list; 
    } 

    public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
} 

El compilador se queja de la línea con el para-cada uno: "No coincide el tipo no puede convertir de tipo de elemento de objeto de cadena."
Si realizo este cambio sutil, que compila:

public static void main(String...a){ 
    Generics<?> generics = new Generics(new Object()); 
    for(String s : generics.getList()){ 
    System.out.println(s); 
    } 
} 

getList() hace uso de genéricos, sino que los utiliza en lo que creía que era una manera completamente ajenos. Podría entender esto si estuviera tratando de iterar sobre algo de tipo T y getList() devolvió un List<T> o algo así, pero ese no es el caso aquí. El tipo de devolución de getList() no debería tener absolutamente nada que ver con T y no debería importar si uso el tipo sin procesar para mi objeto Generics o no ... ¿no? ¿No deberían estos estar completamente sin relación, o realmente me estoy perdiendo algo aquí?

Tenga en cuenta que el código también compila si hago esto, por lo que pensé que debería haber sido equivalente a la primera, así:

public static void main(String...a){ 
    Generics generics = new Generics(new Object()); 
    List<String> list = generics.getList(); 
    for(String s : list){ 
    System.out.println(s); 
    } 
} 
+1

'' no es diferente de '' . No estás haciendo una versión genérica de tu clase, estás haciendo el tipo sin procesar. Lo que nos lleva a la pregunta de por qué su clase es genérica en primer lugar. El único lugar donde usas T está en el constructor y no usas esa referencia. – unholysampler

+0

Utilicé '' porque solo necesitaba algo para un ejemplo. El código real obviamente es otra cosa, y usa T ... solo usa T de una manera completamente diferente a 'getList()'. –

+0

no relacionado con su pregunta, pero haré que el constructor sea Generics cls) para que no tenga que instanciar un objeto de tipo T solo para construir esta clase de Generics. – MeBigFatGuy

Respuesta

11

La diferencia es que cuando utiliza el tipo sin formato, todos, las referencias genéricas dentro de las firmas miembro también se convierten a sus formularios sin formato. Tan eficazmente que está llamando a un método que ahora tiene una firma así:

List getList() 

Ahora en cuanto a por qué su versión final compila - aunque sí, hay una advertencia si se utiliza -Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion 
    List<String> list = generics.getList(); 
             ^

Esto es similar a:

List list = new ArrayList(); 
List<String> strings = list; 

... que también compila, pero con una advertencia sobre -Xlint.

La moraleja de la historia: ¡no use tipos brutos!

+0

Estoy muy sorprendido de que * todas * las referencias genéricas dentro de las firmas de miembros se conviertan a sus formularios sin formato. ¿Cuál es el razonamiento para hacer eso (además de que Sun simplemente sintió ganas)? –

+4

@Michael: el JLS incluye esta discusión en la sección 4.8 (tipos crudos): "Los tipos primarios están estrechamente relacionados con comodines. Ambos se basan en tipos existenciales. Los tipos crudos pueden considerarse comodines cuyas reglas de tipo son deliberadamente incorrectas, para acomodar interacción con el código heredado ". En otras palabras, los tipos crudos realmente no deberían aparecer en el código nuevo, pero intentaron evitar que el código antiguo no se compilara, incluso si era al menos sospechoso. –

+0

Muy interesante. Ya sabía que no debía usar tipos crudos (un compañero de trabajo escribió el código para declarar la variable), pero esto resalta que realmente puede importar. –

3

Cambie la línea

Generics generics = new Generics(new Object()); 

a

Generics<?> generics = new Generics<Object>(new Object()); 

La raíz de su problema es que está utilizando un raw type por lo que el tipo deEl métodoes List, no List<String>.

+0

Los genéricos NO van a ser genéricos de tipo Cadena ... ese es el punto. La cadena no está relacionada con el tipo de genéricos. Independientemente de T, 'getList()' debería devolver una 'Lista '. –

+0

@Michael McGowan, buen punto. Pero tiene que haber algún tipo asociado con el parámetro de tipo en el punto de declaración. 'Generics generics = new Generics (...);' estaría bien, modulo una advertencia de conversión insegura. –

+0

@Michael McGowan, tenga en cuenta que si todo lo que hizo fue eliminar el parámetro de tipo '' de la declaración de clase, entonces funcionaría. –

-1

He hecho un par de ajustes a su código. Usted ve en su comentario que no necesita Object en su constructor, así que vamos a eliminar eso para evitar confusiones.En segundo lugar, si los genéricos va a ser genérico, inicializar correctamente

Esto es lo que el nuevo principal se vería

public static void main(String...a){ 
    Generics<String> generics = new Generics<String>(); 
    for(String s : generics.getList()){ 
     System.out.println(s); 
    } 
    } 
+0

Los genéricos NO van a ser genéricos de tipo Cadena ... ese es el punto. La cadena no está relacionada con el tipo de genéricos. –

+0

Creo que malinterpretaste mi punto. Si observa el código, tiene el método getList() devolviendo una lista . Si quisiéramos hacer realmente el código limpio podríamos haber eliminado la parte de los genéricos del código que no sea el método getList(). estabas pidiendo ayuda para que esto compilara en lugar de ser su enfoque correcto/incorrecto. – Sean

+0

Si deja la desaceleración genérica en el nivel de clase, abre el método getList() para eliminar la codificación difícil que existe ahora. – Sean

Cuestiones relacionadas