2009-03-19 20 views
27

En este ejemplo simplificado, tengo una clase genérica y un método que devuelve un Mapa independientemente del parámetro de tipo. ¿Por qué el compilador borra los tipos en el mapa cuando no especifico un tipo en la clase contenedora?¿Por qué este código java genérico no se compilará?

import java.util.Map; 

public class MyClass<T> 
{ 
    public Map<String, String> getMap() 
    { 
     return null; 
    } 

    public void test() 
    { 
     MyClass<Object> success = new MyClass<Object>(); 
     String s = success.getMap().get(""); 

     MyClass unchecked = new MyClass(); 
     Map<String, String> map = unchecked.getMap(); // Unchecked warning, why? 
     String s2 = map.get(""); 

     MyClass fail = new MyClass(); 
     String s3 = fail.getMap().get(""); // Compiler error, why? 
    } 
} 

Aparece el error de compilación.

MyClass.java:20: incompatible types 
found : java.lang.Object 
required: java.lang.String 
       String s3 = fail.getMap().get(""); // Compiler error 
+0

¿Cuál es el texto exacto de la advertencia no marcada? – Powerlord

+0

advertencia: [desmarcado] conversión sin marcar –

Respuesta

31

Lo tengo. Esto realmente no es un error, aunque parezca extraño.

De section 4.8 (raw types) of the JLS:

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 crudo tipo C que no se hereda de sus superclases o superinterfaces es la borrado de su tipo en el genérico declaración correspondiente a C. el tipo de un miembro estático de un tipo de prima C es el mismo que su tipo en el genérico declaración correspondiente a C.

Así que, aunque el tipo de firma del método no utiliza ningún tipo de parámetros de la propia clase, patadas tipo de borrado en la firma y se convierte efectivamente en

public Map getMap() 

En otras palabras, creo Puede imaginar un tipo sin procesar como la misma API que el tipo genérico, pero con todos los bits <X> eliminados de en todas partes (en la API, no en la implementación).

EDIT: Este código:

MyClass unchecked = new MyClass(); 
Map<String, String> map = unchecked.getMap(); // Unchecked warning, why? 
String s2 = map.get(""); 

compila porque hay una conversión implícita, pero sin control de la prima Map tipo de Map<String, String>. Se puede obtener el mismo efecto al hacer una conversión explícita (que no hace nada en tiempo de ejecución) en el último caso:

// Compiles, but with an unchecked warning 
String x = ((Map<String, String>)fail.getMap()).get(""); 
+1

Eso es estúpido. Gracias Jon! –

+2

Hm ... tiene algún sentido ... pero es increíblemente estúpido al mismo tiempo ... –

+2

No es tan estúpido en mi opinión. – alexmeia

3

Hm ... desafortunadamente no puedo decirte por qué falla. Pero puedo darle una solución simple:

Cambie el tipo de fail a MyClass<?>, luego se compilará muy bien.

+0

Un molde funciona bien también. ¿Por qué un tipo de? afectar el tipo de mapa? Todavía no tiene sentido para mí. –

+1

@Motlin: Jon lo explicó bastante bien: MyClass no es un tipo crudo. –

+0

Ahora que entiendo por qué funciona su solución, voy a usarla en lugar de un molde. Gracias. –

1

tipos genéricos se borran después de compilar.

Al hacer:

Map<String, String> map = unchecked.getMap(); 

que estés forzando un elenco de un mapa a < String, String>, y por eso la advertencia sin marcar. Sin embargo, después de que usted puede hacer:

String s2 = map.get(""); 

debido mapa es de tipo Mapa < String, String>.

Sin embargo, cuando se hace

String s3 = fail.getMap().get(""); 

no estés enviando fail.getMap() para nada, por lo que se considera que es claramente mapa, no se asignan < String, String>.

Lo que debe hacer en este último es algo así como:

String s3 = ((Map<String, String>fail.getMap()).get(""); 

que seguirá lanzado una advertencia, pero funcionará de todos modos.

+2

Entonces, ¿por qué compila el primero? "Borrar" no es la respuesta completa aquí. –

+0

Sí, tienes razón. Creo que la respuesta de Jon es la mejor aquí. – Seb

2

Pregunta muy interesante y respuesta muy interesante de Jon Skeet.

Solo quiero agregar algo acerca de la estupidez o no estupidez de este comportamiento del compilador de Java.

Creo que el compilador supone que si no especifica el parámetro de tipo en una clase generc, no puede (o no quiere) utilizar ningún parámetro de tipo en absoluto. Podrías usar una versión de Java anterior a 5, o te encantaría hacer moldes manualmente.

No me parece tan estúpido.

Cuestiones relacionadas