Génériques Java (caractères génériques)


109

J'ai quelques questions sur les caractères génériques génériques en Java:

  1. Quelle est la différence entre List<? extends T>et List<? super T>?

  2. Qu'est-ce qu'un joker borné et qu'est-ce qu'un joker illimité?


Dans le cas où la réponse de Ted Gao serait supprimée (puisqu'il s'agissait d'un lien uniquement), voici le billet de blog auquel elle était liée.
royhowie

Réponses:


123

Dans votre première question, <? extends T>et <? super T>sont des exemples de caractères génériques délimités. Un joker illimité ressemble <?>, et signifie essentiellement <? extends Object>. Cela signifie vaguement que le générique peut être de n'importe quel type. Un caractère générique borné ( <? extends T>ou <? super T>) place une restriction sur le type en disant qu'il doit soit étendre un type spécifique ( <? extends T>est connu comme une limite supérieure), soit doit être un ancêtre d'un type spécifique ( <? super T>est connu comme une limite inférieure) .

Les didacticiels Java ont de très bonnes explications sur les génériques dans les articles Wildcards et More Fun with Wildcards .


Juste pour bien faire les choses, si A <B et B <C alors: <A étend C> est faux?
Pablo Fernandez

Si par A <B, vous voulez dire que A étend B, alors A étend C. Vous n'utiliseriez pas cela dans la syntaxe générique, vous diriez <? étend C> pour limiter vos choix à A ou B.
Bill the Lizard

et dans ce cas si je dis <? super C> quelle serait la différence?
Pablo Fernandez

3
Je voulais juste recommander une autre référence sur Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena

3
@Pablo: <? super C>signifierait que votre type est limité à quelque chose ci-dessus Cdans la hiérarchie des types. (Désolé pour la réponse extrêmement tardive. Je suppose que nous n'avons pas eu de notifications de commentaires il y a 2 ans?)
Bill the Lizard

49

Si vous avez une hiérarchie de classes A, B est une sous-classe de A, et C et D sont tous deux une sous-classe de B comme ci-dessous

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

ensuite

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Un caractère générique borné est comme ? extends Bsi B est un type. Autrement dit, le type est inconnu mais une "borne" peut être placée dessus. Dans ce cas, il est délimité par une classe, qui est une sous-classe de B.


Je suppose que List<? super B>décrit comme List accepte le type qui est la classe parente de la classe B ? C'est pourquoi C et D ne compilent pas hmm?
Volkan Güven

38

Josh Bloch a également une bonne explication sur le moment de l'utiliser superet extendsdans cette conférence vidéo google io où il mentionne le mnémonique Producer extendsConsumersuper .

À partir des diapositives de la présentation:

Supposons que vous souhaitiez ajouter des méthodes en bloc à Stack<E>

void pushAll(Collection<? extends E> src);

- src est un producteur E

void popAll(Collection<? super E> dst);

- dst est un consommateur E


J'ai lu le livre de Bloch, mais je ne vois toujours pas la différence entre étend et super dans ce cas particulier.
Pablo Fernandez

Regardez la vidéo, je pense que c'est assez clair. Aussi, je pense que vous devriez poser une autre question sur ce "quelle est la différence entre List <? Extend T> et List <? Super T>" où vous obtiendrez, espérons-le, plus de réponses. (si vous le faites, ajoutez un lien à partir d'ici)
vide

2
Lits - pourquoi ne pas inclure l'exemple dans cette réponse pour la rendre complète et autonome. Les liens sont éphémères.
James Schek

3

Il peut arriver que vous souhaitiez restreindre les types de types autorisés à être transmis à un paramètre de type. Par exemple, une méthode qui opère sur des nombres peut vouloir uniquement accepter des instances de Number ou de ses sous-classes. C'est à cela que servent les paramètres de type borné.

Collection<? extends MyObject> 

signifie qu'il peut accepter tous les objets qui ont une relation IS- A avec MyObject (c'est-à-dire tout objet qui est un type de myObject ou nous pouvons dire tout objet de n'importe quelle sous-classe de MyObject) ou un objet de la classe MyObject.

Par exemple:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Ensuite,

Collection<? extends MyObject> myObject; 

n'acceptera que MyObject ou les enfants de MyObject (c'est-à-dire tout objet de type OurObject ou YourObject ou MyObject, mais pas tout objet de superclasse de MyObject).


1

En général,

Si une structure contient des éléments avec un type de formulaire ? extends E, nous pouvons extraire des éléments de la structure, mais nous ne pouvons pas mettre d'éléments dans la structure

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Pour mettre des éléments dans la structure, nous avons besoin d'un autre type de caractère générique appelé Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }

1

Des caractères génériques génériques sont créés pour rendre les méthodes qui fonctionnent sur Collection plus réutilisables.

Par exemple, si une méthode a un paramètre List<A>, nous ne pouvons donner List<A>qu'à cette méthode. C'est un gaspillage pour la fonction de cette méthode dans certaines circonstances :

  1. Si cette méthode ne lit que des objets à partir de List<A>, alors nous devrions être autorisés à donner List<A-sub>à cette méthode. (Parce que A-sub EST un A)
  2. Si cette méthode n'insère que des objets dans List<A>, alors nous devrions être autorisés à donner List<A-super>à cette méthode. (Parce que A EST un A-super)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.