2010-03-02 5 views
15

J'ai appris et expérimenté avec Java Generics pendant un moment, mais j'ai rencontré quelque chose que je ne peux pas expliquer. Prenons par exemple le code suivant:Classes internes typées génériques 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) { 
     } 
    } 
} 

Et le code de test:

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(""); 
    } 
} 

Lorsque cela est exécuté, il donne un message d'erreur merveilleusement cryptique:

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 

Maintenant, grâce à une expérimentation J'ai travaillé comment empêcher l'erreur de compilateur. Je peux soit faire de la classe Sub une classe interne statique, soit faire référence à la classe Sub sous la forme Question.Sub <String>. Ce que je ne peux pas faire, c'est expliquer pourquoi j'ai besoin de faire ça.

J'ai lu la documentation Java sur Generics mais aucune ne couvre ce cas particulier.

Quelqu'un peut-il expliquer pourquoi le code est un type incompatible dans sa forme actuelle?

-Edit-

En regardant ce plus je vois que je reçois le même comportement à l'extérieur de Netbeans. Si je le code dans la structure suivante:

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

Quand je compile les fichiers ensemble, je ne suis pas l'erreur:

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

C:\> 

Cependant, quand je compile question d'abord, puis réponse, je obtenir l'erreur:

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 

J'ai entendu parler de l'effacement de type. Est-ce le cas dans cette situation?

+0

Ce code fonctionne pour moi dans Eclipse. Quel IDE/compilateur utilisez-vous? – polygenelubricants

+0

Je peux aussi compiler sans problème en utilisant la version java "1.6.0_15" – Steen

+0

Netbeans 6.7.1 avec JDK 1.5.0_14. Si je compile en dehors de Netbeans je suis d'accord, il compile bien. Je vais enquêter plus loin. Merci pour les commentaires. – gencoreoperative

Répondre

1

L'effacement de type est une propriété de la façon dont les génériques sont actuellement implémentés en Java. Cela signifie que le type des variables n'est connu que lors de la compilation, mais pas au moment de l'exécution. Ainsi, par exemple, dans les domaines suivants:

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

le compilateur sait vérifier contre les éléments étant mis en à une chaîne/base String. Cependant, le code compilé ne sait rien au sujet de la chaîne, String - vous pouvez toujours insérer des objets dans le mauvais type, par exemple:

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

Le problème est que le code compilé ne vérifie pas pour les types d'arguments lors de la transmission, et pas plus que le temps d'exécution (puisque les informations de type ont été effacées, par conséquent, tapez l'effacement).

Je doute que l'effacement de type soit un problème ici - puisque lors de la compilation, vous avez l'information de type complète - mais plutôt que c'est probablement un bug. Il y a pas mal de problèmes avec les génériques (à partir d'une implémentation) et il y a différents compilateurs utilisés avec JavaC et Eclipse, donc il pourrait y avoir des bogues différents. Dans certains cas, le compilateur Eclipse a été plus fidèle à la spécification que le compilateur Sun (donc Eclipse crée des erreurs alors que Sun ne le fait pas) et c'est principalement dû à la complexité du fonctionnement du système de types.

Donc c'est probablement un (ou plusieurs) bugs avec des génériques dans le compilateur 1.5.0_14 ...

+0

Je peux confirmer cela est le cas, voir ci-dessous: C: \> javac -version javac 1.6.0_18 C: \> génériques javac src \ examples \ Question.java C: \> génériques javac src \ examples \ réponse. java -cp src Fonctionne très bien. Je vous remercie! – gencoreoperative

+0

En fait "Ce que cela signifie, c'est que le type des variables est seulement connu au moment de la compilation, mais pas au moment de l'exécution": les informations Type sont disponibles au moment de l'exécution et disponibles en utilisant la réflexion. Dans les fichiers de classe, les types génériques sont toujours conservés (sinon, comment compilera-t-il une classe externe avec des génériques), donc "effacement de type" n'efface pas les types de la classe, seulement à partir du runtime. – Pindatjuh

+0

Pour être clair; les informations sur les types génériques sont stockées dans des annotations sur la classe, mais pas sur les types de la méthode. Ainsi, tout appel non générique peut toujours être utilisé avec celui-ci; cependant, les annotations sont uniquement utilisées par le compilateur au moment de la compilation. Donc, ils ne font pas vraiment partie du runtime, même si vous pouvez introspecter le fichier de classe à l'exécution. – AlBlue

Questions connexes