Dans cet exemple:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile()
ne parvient pas à compiler avec:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
while compiles()
est accepté par le compilateur.
Cette réponse explique que la seule différence est que, contrairement à <? ...>
, <T ...>
vous permet de référencer le type plus tard, ce qui ne semble pas être le cas.
Quelle est la différence entre <? extends Number>
et <T extends Number>
dans ce cas et pourquoi la première compilation ne se fait-elle pas?
Réponses:
En définissant la méthode avec la signature suivante:
et l'invoquer comme:
Dans le jls §8.1.2, nous trouvons que (partie intéressante en gras pour moi):
En d'autres termes, le type
T
est comparé au type d'entrée et affectéInteger
. La signature deviendra effectivementstatic void compiles(Map<Integer, List<Integer>> map)
.En matière de
doesntCompile
méthode, jls définit des règles de sous-typage ( §4.5.1 , mis en gras par moi):Cela signifie, en
? extends Number
effet, contientInteger
ouList<? extends Number>
contientList<Integer>
, mais ce n'est pas le cas pourMap<Integer, List<? extends Number>>
etMap<Integer, List<Integer>>
. Plus sur ce sujet peut être trouvé dans ce fil SO . Vous pouvez toujours faire fonctionner la version avec?
caractère générique en déclarant que vous attendez un sous-type deList<? extends Number>
:la source
? extends Number
plutôt que? extends Numeric
. [2] Votre affirmation selon laquelle "ce n'est pas le cas pour List <? Extend Number> et List <Integer>" est incorrecte. Comme @VinceEmigh l'a déjà souligné, vous pouvez créer une méthodestatic void demo(List<? extends Number> lst) { }
et l'appeler comme cecidemo(new ArrayList<Integer>());
ou celademo(new ArrayList<Float>());
, et le code se compile et s'exécute OK. Ou suis-je peut-être mal interprété ou mal compris ce que vous avez déclaré?List<? extends Number>
comme paramètre de type de la carte entière, pas elle-même. Merci beaucoup pour le commentaire.List<Number>
ne contient pasList<Integer>
. Supposons que vous ayez une fonctionstatic void check(List<Number> numbers) {}
. Lorsque l'invocation aveccheck(new ArrayList<Integer>());
elle ne se compile pas, vous devez définir la méthode commestatic void check(List<? extends Number> numbers) {}
. Avec la carte, c'est la même chose mais avec plus d'imbrication.Number
un paramètre de type de liste et que vous devez ajouter? extends
pour le rendre covariant,List<? extends Number>
est un paramètre de type deMap
et a également besoin? extends
de covariance.Dans l'appel:
T correspond à Integer, donc le type de l'argument est a
Map<Integer,List<Integer>>
. Ce n'est pas le cas pour la méthodedoesntCompile
: le type de l'argument resteMap<Integer, List<? extends Number>>
quel que soit l'argument réel dans l'appel; et ce n'est pas attribuable à partir deHashMap<Integer, List<Integer>>
.MISE À JOUR
Dans la
doesntCompile
méthode, rien ne vous empêche de faire quelque chose comme ceci:Alors évidemment, il ne peut pas accepter un
HashMap<Integer, List<Integer>>
comme argument.la source
doesntCompile
? Juste curieux à ce sujet.doesntCompile(new HashMap<Integer, List<? extends Number>>());
fonctionnerait, tout commedoesntCompile(new HashMap<>());
.HashMap<Integer, List<Integer>>
" pourriez-vous expliquer pourquoi il ne peut pas être attribué à partir de cela?Exemple simplifié de démonstration. Le même exemple peut être visualisé comme ci-dessous.
List<Pair<? extends Number>>
est un type de caractères génériques à plusieurs niveaux alors queList<? extends Number>
c'est un type de caractère générique standard.Les instanciations concrètes valides du type générique
List<? extends Number>
incluentNumber
et tout sous-typeNumber
alors que dans le casList<Pair<? extends Number>>
où il s'agit d'un argument de type argument de type et a lui-même une instanciation concrète de type générique.Les génériques sont invariants donc
Pair<? extends Number>
le type de caractère générique ne peut accepterPair<? extends Number>>
. Le type interne? extends Number
est déjà covariant. Vous devez rendre le type englobant comme covariant pour autoriser la covariance.la source
<Pair<Integer>>
ne fonctionne pas<Pair<? extends Number>>
mais fonctionne avec<T extends Number> <Pair<T>>
?T
contre?
. Une partie du problème est que lorsque Andronicus atteint le point essentiel de son explication, il s'en remet à un autre fil qui n'utilise que des exemples triviaux. J'espérais obtenir une réponse plus claire et plus complète ici.Je vous recommande de consulter la documentation des caractères génériques génériques, en particulier les directives d'utilisation des caractères génériques
Franchement, votre méthode #doesntCompile
et appeler comme
Est fondamentalement incorrect
Ajoutons l' implémentation juridique :
C'est très bien, parce que Double étend Number, donc
List<Double>
c'est tout aussi bienList<Integer>
, non?Cependant, pensez-vous toujours qu'il est légal de passer ici
new HashMap<Integer, List<Integer>>()
de votre exemple?Le compilateur ne le pense pas et fait de son mieux pour éviter de telles situations.
Essayez de faire la même implémentation avec la méthode #compile et le compilateur ne vous permettra évidemment pas de mettre une liste de doubles dans la carte.
Fondamentalement, vous ne pouvez rien mettre mais
List<T>
c'est pourquoi il est sûr d'appeler cette méthode avecnew HashMap<Integer, List<Integer>>()
ounew HashMap<Integer, List<Double>>()
ounew HashMap<Integer, List<Long>>()
ounew HashMap<Integer, List<Number>>()
.Donc, en un mot, vous essayez de tricher avec le compilateur et il se défend assez contre une telle tricherie.
NB: la réponse postée par Maurice Perry est absolument correcte. Je ne suis pas sûr que ce soit assez clair, alors j'ai essayé (j'espère vraiment que j'ai réussi) d'ajouter un article plus complet.
la source