54 votes

Pourquoi utiliser Collections.emptySet () avec des génériques fonctionne-t-il dans l'affectation, mais pas comme paramètre de méthode?

Donc, j'ai une classe avec un constructeur comme ceci:

public FilterList(Set<Integer> labels) {
    ...
}

et je veux construire un nouveau FilterList objet avec un ensemble vide. Suivant Joshua Bloch conseils dans son livre Efficace Java, je ne veux pas créer un nouvel objet pour l'ensemble vide; je vais juste utiliser Collections.emptySet() à la place:

FilterList emptyList = new FilterList(Collections.emptySet());

Cela me donne une erreur, en se plaignant qu' java.util.Set<java.lang.Object> n'est pas un java.util.Set<java.lang.Integer>. OK, comment à ce sujet:

FilterList emptyList = new FilterList((Set<Integer>)Collections.emptySet());

Cela me donne aussi une erreur! Ok, comment à ce sujet:

Set<Integer> empty = Collections.emptySet();
FilterList emptyList = new FilterList(empty);

Hey, ça fonctionne! Mais pourquoi? Après tout, Java n'a pas l'inférence de type, qui est pourquoi vous obtenez une unchecked conversion d'avertissement si vous ne Set<Integer> foo = new TreeSet() au lieu de Set<Integer> foo = new TreeSet<Integer>(). Mais Set<Integer> empty = Collections.emptySet(); fonctionne sans même un avertissement. Pourquoi est-ce?

119voto

Andrzej Doyle Points 52541

La réponse courte est - ce que c'est une limitation de l'inférence de type en Java générique du système. Il est possible de déduire les types génériques contre les variables concrètes, mais pas contre les paramètres de la méthode.

Je soupçonne c'est parce que les méthodes sont expédiés de manière dynamique en fonction de la classe d'exécution de l'objet propriétaire, donc au moment de la compilation (lorsque toutes les informations génériques est résolu) vous ne pouvez pas vraiment sûr de ce que la classe du paramètre de méthode et ne peut donc pas en déduire. Les déclarations de variables sont agréable et constante, de sorte que vous pouvez.

Quelqu'un d'autre pourrait être en mesure de donner plus de détails et/ou un lien sympa. :-)

Dans tous les cas, vous pouvez toujours spécifier les paramètres de type explicite le générique appelle comme ça:

Collections.<Integer>emptySet();

ou même de plusieurs paramètres à la fois, par ex.

Collections.<String, Boolean>emptyMap(); // Returns a Map<String, Boolean>

Cela a souvent l'air un peu plus propre que d'avoir à lancer, dans les cas où l'inférence n'est pas un coup de pied dans.

7voto

essayer

 FilterList emptyList = new FilterList(Collections.<Integer>emptySet());
 

Vous pouvez forcer le paramètre de type pour les méthodes qui en ont, dans les cas où l'inférence n'est pas assez bonne, ou pour vous permettre d'utiliser des sous-types; par exemple:

 // forces use of ArrayList as parameter instead of the infered List
List<String> l = someObject.<ArrayList<String> methodThatTakesTypeParamForReturnType();
 

6voto

jdmichal Points 6283

Vous voulez faire ceci:

 FilterList emptyList = new FilterList(new Collections.<Integer>emptySet());
 

Cela indique à la méthode emptySet que son paramètre générique doit explicitement être utilisé par Integer au lieu de la valeur par défaut Object . Et oui, la syntaxe est complètement funky et non intuitive pour cela. :)

5voto

kasperjj Points 2726

Java a une inférence de type, elle est juste assez limitée. Si vous souhaitez savoir exactement comment cela fonctionne et quelles sont ses limitations, voici une très bonne lecture:

http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html#Type%2BArgument%2BInference

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X