2010-03-02 13 views
15

He estado aprendiendo y experimentando con Java Generics por un tiempo, pero me encontré con algo que no puedo explicar. Tomemos como ejemplo el siguiente código:Genérico Typed Inner Classes en Java

public class Question { 
    public <T> Sub<T> getSub(Class<T> c) { 
     return new Sub<T>(c); 
    } 
    public class Sub<S> { 
     private Class<S> c; 
     public Sub(Class<S> c) { 
      this.c = c; 
     } 
     public void add(S s) { 
     } 
    } 
} 

Y el código de prueba:

import generics.Question.Sub; 

public class Answer { 
    public static void main(String [] args) { 
     Question q = new Question(); 
     Sub<String> s = q.getSub(String.class); 
     s.add(""); 
    } 
} 

Cuando esto se ejecuta se da un mensaje de error maravillosamente críptica:

C:\Answer.java:8: incompatible types 
found : generics.Question.Sub<java.lang.String> 
required: generics.Question.Sub<java.lang.String> 
     Sub<String> s = q.getSub(String.class); 
1 error 

Ahora, a través de algunos experimentos He resuelto cómo prevenir el error del compilador. Puedo hacer que la clase Sub sea una clase interna estática, o necesito referirme a la clase Sub como Question.Sub <String>. Lo que no puedo hacer es explicar por qué tengo que hacer esto.

He leído un poco la documentación de Java sobre genéricos, pero ninguno cubre este caso en particular.

¿Alguien puede explicar por qué el código es un tipo incompatible en su forma actual?

operación -Editar-

En cuanto a esta más cerca que puedo ver que me sale el mismo comportamiento fuera de Netbeans. Si tengo el código de la siguiente estructura:

generics\ 
generics\Question.java 
generics\Answer.java 

Cuando puedo compilar los archivos juntos, que no entiendo el error:

C:\>javac generics\Question.java generics\Answer.java 

C:\> 

Sin embargo, cuando compilo pregunta primero y luego responder, Obtenga el error:

C:\>javac generics\Question.java 

C:\>javac generics\Answer.java 
generics\Answer.java:8: incompatible types 
found : generics.Question.Sub<java.lang.String> 
required: generics.Question.Sub<java.lang.String> 
     Sub<String> s = q.getSub(String.class); 
           ^
1 error 

He escuchado algo acerca de Type Erasure. ¿Es este el caso en esta situación?

+0

Este código funciona para mí en Eclipse. ¿Qué compilador IDE estás usando? – polygenelubricants

+0

También puedo compilar esto sin problemas usando la versión de Java "1.6.0_15" – Steen

+0

Netbeans 6.7.1 con JDK 1.5.0_14. Si compilo fuera de Netbeans, estoy de acuerdo, compila bien. Investigaré más a fondo. Gracias por la respuesta. – gencoreoperative

Respuesta

1

La borradura de tipo es una propiedad de la forma en que los genéricos se implementan actualmente en Java. Lo que esto significa es que el tipo de variables solo se conoce en tiempo de compilación, pero no en tiempo de ejecución. Así, por ejemplo, en el siguiente:

Map<String,String> map = new HashMap<String,String>(); 

entonces el compilador sabe para comprobar contra los artículos que se ponen en al forma de cuerda/cadena. Sin embargo, el código compilado no sabe nada sobre el String, String - todavía se puede insertar objetos con el tipo equivocado, por ejemplo:

Map other = (Map)map; 
other.put(new Integer(3), new Double(4.5); 

El problema es que el código compilado no comprueba los tipos de argumentos al pasar, y tampoco el tiempo de ejecución (ya que la información del tipo ha sido borrada, por lo tanto, escriba borrado).

Dudo que el borrado de tipo sea un problema aquí, ya que en tiempo de compilación, tiene toda la información de tipo, sino que probablemente sea un error. Hay varios problemas peludos con los genéricos (a partir de una implementación) y existen diferentes compiladores en uso con JavaC y Eclipse, por lo que pueden presentar diferentes errores. En algunos casos, el compilador de Eclipse ha sido más fiel a la especificación que el compilador de Sun (por lo que Eclipse crea errores mientras que Sun no) y se debe principalmente a la complejidad de la forma en que funciona el sistema de tipos.

Así que es muy probable que haya uno (o más) errores con genéricos en el compilador 1.5.0_14 ...

+0

puedo confirmar que este es el caso, ver a continuación: C: \> javac -version javac 1.6.0_18 C: \> javac src \ \ genéricos Question.java C: \> javac src \ genéricos \ respuesta. java -cp src Funciona bien. ¡Gracias! – gencoreoperative

+0

En realidad "Lo que esto significa es que el tipo de variables solo se conoce en tiempo de compilación, pero no en tiempo de ejecución": la información de tipo está disponible en tiempo de ejecución y está disponible mediante el uso de la reflexión. En los archivos de clase, aún se guardan los tipos genéricos (cómo se compilará contra una clase externa con genéricos), por lo tanto, "borrado de tipo" no borrará los tipos de la clase, solo del tiempo de ejecución. – Pindatjuh

+0

Para ser claro; la información sobre los tipos genéricos se almacena en anotaciones en la clase, pero no en los tipos del método. Por lo tanto, cualquier llamada no genérica aún se puede usar con ella; sin embargo, las anotaciones solo las usa el compilador en el momento de la compilación. Así que no son realmente parte del tiempo de ejecución, incluso si puedes introspectar el archivo de clase en tiempo de ejecución. – AlBlue