3 votes

Génériques - Pourquoi certaines versions ne fonctionnent-elles pas ?

Ce code est tiré du manuel de l'OCP. Pourquoi les versions 1 et 2 de la méthode addSound ne compilent-elles pas, alors que les versions 3 et 4 le font ? Le message d'erreur de compilation est "add(capture ) in List cannot be applied to java.lang.String".

public static void main(String[] args) {
    List<String> strings = new ArrayList<>();
    strings.add("tweet");
    List<Object> objects = new ArrayList<>(strings);
    addSound(strings);
    addSound(objects);
}

//version 1
public static void addSound(List<?> list) {
    list.add("quack");
}

//version 2
public static void addSound(List<? extends Object> list) {
    list.add("quack");
}

//version 3
public static void addSound(List<Object> list) {
    list.add("quack");
}

//version 4
public static void addSound(List<? super String> list) {
    list.add("quack");
}

3voto

slim Points 12620

Ce qu'il faut se demander, c'est si le compilateur peut être certain que list Le type de l'élément est cohérent avec le type de l'élément String :

  1. <?> -- le type est inconnu. Il pourrait s'agir d'un Integer par exemple. Nous ne pouvons pas savoir que String s'adapte. new List<Integer>().add("quack") ne se compilerait pas, donc ce n'est pas possible non plus.
  2. <? extends Object> -- encore une fois, le type est inconnu. Toutes les classes étendent Object . Il pourrait donc s'agir à nouveau de Integer .
  3. <Object> - Pas de génériques ici. String est une sous-classe de Object . new List<Object>().add("quack") fonctionne, ce qui permet de compiler.
  4. <? super String> est inconnue, à la condition (" limites ") que c'est une superclasse de String . Puisque nous savons que String extends Object ce type ne peut être que Object o String . List<Object>().add("quack") est OK, et List<String>().add("quack") est OK, donc cela compile.

Le raisonnement du point 4 s'applique bien sûr à des hiérarchies de types plus complexes :

BufferedInputStream s'étend FilterInputStream s'étend InputStream s'étend Object . InputStream met en œuvre les interfaces Closeable y AutoCloseable .

Donc <? super FilterInputStream> correspondances Object , InputStream , FilterInputStream , Closeable y AutoCloseable .

Ainsi, un List<? super FilterInputStream> l pourrait être un List<Closeable> ou il peut s'agir d'un List<InputStream >, etc. Mais tout ce dont vous êtes sûr, c'est que vous pouvez l.add(new FilterInputStream()) ou, puisqu'il s'agit d'une sous-classe l.add(new BufferedInputStream())

2voto

Murat Karagöz Points 13113

En Java, il y a la sécurité et l'effacement des types. Ces deux concepts sont la raison pour laquelle les versions 1 et 2 ne fonctionnent pas.

Vous ne pouvez rien ajouter à List<?> puisque vous n'avez aucune idée du type d'objet contenu dans la liste. Il pourrait s'agir d'une liste de Integer o String par exemple.

public static void addSound(List<?> list) {
    list.add("quack"); // does not work since List<?> can be anything - type safety not guaranteed
}

Le compilateur Java supprime tous les types au moment de l'exécution, ce que l'on appelle l'effacement de type. List<String> sera List y List<Integer> sera également List - Il n'est donc pas possible de les différencier. Vous pourriez le voir dans le bytecode, mais cela n'a rien à voir ici. C'est la raison pour laquelle la version 2 ne fonctionne pas.

//version 2 - has the same type erasure as version 3
public static void addSound(List<? extends Object> list) {
    list.add("quack");
}

//version 3
public static void addSound(List<Object> list) {
    list.add("quack");
}

La version 2 ne fonctionne pas non plus à cause de la sécurité des types. Voir cette réponse pour limites génériques

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