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.