2010-07-23 10 views
5

¿Podría alguien explicarme por qué hay una necesidad explícita de asignar un tipo genérico para la instancia de ForEachLoop?Genéricos en cada problema de bucle, si la instancia no tiene el tipo genérico asignado

Por qué compilador se queja: coinciden los tipos: no se puede convertir de tipo de elemento de objeto de cadena?

JDK 1.5.0_09

import java.util.ArrayList; 
import java.util.Collection; 

public class ForEachLoop<T> { 

public static void main(String[] args) { 

    // Non functional version 
    ForEachLoop f = new ForEachLoop(); 

    // Functional version 
    //ForEachLoop<Integer> f = new ForEachLoop(); 

      // Type mismatch: cannot convert from element type Object to String 
    for(String a : f.getStrings()) { 
     System.out.println(a); 
    } 
} 

public Collection<String> getStrings() { 
    Collection<String> strings = new ArrayList<String>(); 
    strings.add("Hello"); 
    return strings; 
} 

} 

Respuesta

8

Esto es un error bastante común:

ForEachLoop f = new ForEachLoop(); 

debería ser

ForEachLoop<Something> f = new ForEachLoop<Something>(); 

Si se utiliza el tipo de prima (que no debe) el compilador borrará toda la información genérica para esa instancia, incluso si no es el parámetro de tipo T, para que sea compatible con código pre 1.5.

Solo use tipos sin formato si está escribiendo para Java 1.4 o menos, en cuyo caso no debería tener genéricos de ningún tipo. En el nivel de bytecode, el método devuelve una colección (en bruto) después de borrar el tipo. Normalmente, si la instancia tiene el conjunto de tipos genérico, cuando intenta hacer get en la colección, el compilador usará la información genérica para decidir que debe devolver una Cadena, y luego en el nivel de código de bytes arrojará automáticamente el Objeto que recibe de la Colección a Cadena (ya que se garantiza que es una Cadena). Pero si usa el tipo sin formato, el compilador ignorará toda la información genérica y ya no volverá a enviar el objeto automáticamente.

Editar: En la sección de Tipos primas hay estas cosas:

Otra implicación de las reglas anteriores es que una clase interna genérica de un crudo tipo puede por sí mismo sólo se puede utilizar como una prima tipo :

class Outer<T>{ 
    class Inner<S> { 
    S s; 
    } 
} 

no es posible acceder a la interior como tipo parcialmente en bruto (un tipo "raro")

Outer.Inner<Double> x = null; // illegal 
Double d = x.s; 

porque la propia Outer es crudo, por lo que son todas sus clases internas, incluyendo interior, y por lo que no es posible pasar cualquier parámetros de tipo a ella.

El uso de tipos sin procesar solo se permite como una concesión a la compatibilidad del código heredado . Se desaconseja encarecidamente el uso de tipos sin procesar en el código escrito después de la introducción de la geniticidad en el lenguaje de programación de Java. Es es posible que las versiones futuras de el lenguaje de programación Java no permitan el uso de tipos sin procesar.

Es un error en tiempo de compilación intentar utilizar un tipo de miembro de tipo parametrizado como tipo sin formato.

Esto significa que la prohibición de "raros" tipos extiende al caso en que el tipo de clasificación se parametriza, pero que intenta utilizar la clase interna como tipo prima:

Outer<Integer>.Inner x = null; // illegal 

Este es lo opuesto al caso en que se discutió anteriormente. No hay ninguna justificación práctica para este tipo medio horneado . En el código heredado, no se utilizan parámetros de tipo . En el código no heredado, debemos usar los tipos genéricos correctamente y pasar todos los parámetros de tipo real requeridos.

Observe que la clase Inner tiene su propio parámetro de tipo independiente de la de la clase Externa, y todavía se borra. Básicamente no quieren que mezclemos tipos crudos y genéricos en la misma instancia, ya que no tiene sentido en ninguna versión (en pre 1.5, la parte genérica será un error, en 1.5+ no se recomienda el tipo sin formato, y incluso puede ser removido de las versiones futuras)

Luego está también esto:

El tipo de un constructor (§8.8), método de instancia (§8.8, §9.4), o campo no estático (§8.3) M de un bruto tipo C que no se hereda de sus superclases o superinterfaces es el borrado de su tipo en la declaración genérica correspondiente a C. El tipo de un miembro estático de un tipo de prima C es el mismo que su tipo en la declaración genérica correspondiente a C.

Es un error de tiempo de compilación para pasar parámetros de tipo real a un miembro de tipo no estático de un tipo sin formato que no es heredado de sus superclases o superinterfaces .

que dice que los constructores, los métodos de instancia y los campos no estáticos se tratarán como sin procesar en una instancia sin formato. Los miembros estáticos se tratarán como genéricos de todos modos, ya que no requieren el acceso de una instancia.

+0

suena interesante y lógica. ¿Podría indicarme la especificación java de que "el compilador borrará toda la información genérica incluso si no es el parámetro de tipo T"? –

+0

Agregué algunos fragmentos del JLS y mi comprensión (quizás limitada) de ellos. –

+0

Intentaré absorber tu respuesta, pero me llevará un tiempo. Gracias –

0

ForEachLoop<String> f = new ForEachLoop<String>();

0

¿Qué esperas que sucedería en la versión no funcional? Ha declarado una clase de plantilla ForEachLoop<T>, que es un tipo incompleto hasta que esté completamente calificado. Esto no cambia simplemente porque

  • su código está dentro de la clase de plantilla
  • que en realidad no utiliza el parámetro de plantilla.
+0

Esperaría que el código sea compilable incluso si no especifico el parámetro T ya que getStrings() no depende de T. –

+0

Bueno, me gusta un compilador que no intente adivinar lo que quiero decir. En la mayoría de los casos, si un parámetro de plantilla no se usa, sería porque me perdí algo. (Si desea la máxima permisividad, intente Perl en la vieja escuela en lugar de Java) –

0

genéricos de Java se implementan utilizando Type Erasure, lo que significa que el compilador emitirá código para cada instanciación de un tipo genérico (por ejemplo ForEachLoop<Something>) que utiliza básicamente el "fundido a objeto" idioma. Por lo tanto, su línea ForEachLoop f = new ForEachLoop(); generará una instanciación "directa" donde el argumento de tipo predeterminado es object.

Entonces, lo que realmente tiene es un ForEachLoop<object>, por lo que obtiene el error de fundición.

+0

No usa el tipo genérico, de hecho, ForEachLoop también debería funcionar. –

+0

¿Y dónde sería eso para T? – musiKk

+0

@Musikk: de hecho, el elenco a T vive en cada ocasión "T se usa como T" (por ejemplo, en un método de retorno tipo T). –

1

La expresión equivalente de que para el bucle es (según la especificación del lenguaje Java):

for (Iterator<String> i = f.getStrings().iterator(); i.hasNext();) { 
    String a = i.next(); 
    System.out.println(a); 
} 

bucles Foreach es una clase genérica, pero f que utiliza el tipo de prima. Entonces, el compilador tiene que suponer que el iterador puede devolver Object instancias o instancias de subtipos. Y da una advertencia, porque asigna ese iterador sin formato a un iterador parametrizado.

Siempre que su colección dentro de la instancia de ForEachLoop solo contenga cadenas, no verá un error de tiempo de ejecución.

para deshacerse de la advertencia, parametrizar la parte de instancias, como:

ForEachLoop<String> f = new ForEachLoop<String>(); 
+0

Cualquier parametrización debería funcionar, incluso con

+0

@Georgy - claro, pero el código en su pregunta usó '', así que elegí el mismo tipo en mi respuesta. –

Cuestiones relacionadas