web-dev-qa-db-fra.com

Comprendre les limites supérieure et inférieure? dans les génériques Java

J'ai vraiment du mal à comprendre le paramètre joker. J'ai quelques questions à ce sujet. 

  1. ? en tant que paramètre de type ne peut être utilisé que dans des méthodes. Exemple: printAll(MyList<? extends Serializable>) Je ne peux pas définir de classes avec ? en tant que paramètre de type. 

  2. Je comprends la limite supérieure sur ?. printAll(MyList<? extends Serializable>) signifie: "printAll imprimera MyList s'il contient des objets qui implémentent l'interface Serialzable."
    J'ai un problème avec le super. printAll(MyList<? super MyClass>) signifie: "printAll imprimera MyList s'il comporte des objets de MyClass ou toute classe qui s'étend MyClass (les descendants de MyClass).

Corrigez-moi où je me suis trompé. 

En bref, seuls T ou E ou K ou V ou N peuvent être utilisés comme paramètres de type pour définir des classes génériques. ? ne peut être utilisé que dans les méthodes


Mise à jour 1:  

public void printAll(MyList<? super MyClass>){
    // code code code
}

Selon le livre d’Ivor Horton, MyList<? super MyClass> signifie que je peux imprimer MyList si elle contient des objets de MyClass ou l’une des interfaces ou classes qu’elle implémente. C'est-à-dire que MyClass est une borne inférieure . C'est la dernière classe de la hiérarchie d'héritage. Cela signifie que mon hypothèse initiale était fausse.

Donc, si MyClass ressemble à: 

public class MyClass extends Thread implements ActionListener{
    // whatever
}

alors, printAll() imprimera si
1. Il y a des objets de MyClass dans la liste
2. Il y a des objets de Thread ou ActionListener dans la List


Mise à jour 2:  

Donc, après avoir lu les nombreuses réponses à la question, voici ce que je comprends: 

  1. ? extends T signifie toute classe qui étend T. Nous faisons donc référence aux enfants de T. Par conséquent,T est la limite supérieure. La classe la plus haute dans la hiérarchie d'héritage  

  2. ? super T signifie toute classe/interface qui est super sur T. Nous faisons donc référence à tous les parents de T.T est donc la limite inférieure. La classe la plus basse dans la hiérarchie d'héritage

28
Little Child

Tout d'abord, T ou E ou K ou quoi que ce soit ne sont pas des noms fixes. Ce ne sont que des variables de type, et vous en décidez le nom. T, E, K ne sont que des exemples, mais vous pouvez l'appeler Foo ou autre chose.

Passons maintenant à votre première question: puisque le caractère générique ? représente le type "tout type et inconnu", le type non spécifié, il n’a aucun sens de déclarer une classe générique sur un type non spécifié. Il est utile d'avoir des caractères génériques dans les paramètres de méthodes ou dans les variables lorsque le type ne les intéresse pas.

Maintenant, en ce qui concerne votre deuxième question: la limite inférieure donne encore plus de flexibilité à vos méthodes génériques. extends et super sont tous deux l'inverse:

  • ? extends T: un type inconnu qui est un sous-type de T
  • ? super T: un type inconnu qui est un super type de T

Ce dernier peut être utile lorsque vous souhaitez accepter un type compatible avec T (de sorte que T est ce type). Un exemple pratique peut être trouvé ici .

11
Jack

Commençons par le début.

Strictement parlant, tout identificateur Java valide peut être utilisé comme paramètre de type générique - il ne s'agit que d'un type spécial de variable:

public static final class MyGenericClass<MyGenericType> {

}

Est parfaitement valide Java.

Ensuite, vous pouvez utiliser ? partout où vous pouvez faire une déclaration. Vous pouvez utiliser le caractère générique lorsque vous déclarez des variables, mais pas lorsque vous les {instanciez} _:

public static final class MyGenericClass {
    private final Collection<? extends String> myThings;

    public MyGenericClass(Collection<? extends String> myThings) {
        this.myThings = myThings;
    }  

    public void doStuff(final Collection<? extends String> myThings) {

    }
}

Est à nouveau tout valide, vous ne pouvez pas faire ceci:

final Collection<? extends String> myThings = new ArrayList<? extends String>();

Lorsqu'il s'agit de extends contre super, on parle de co-variance vs contre-variance. Il détermine quelle direction dans la hiérarchie de classes les types fournis sont autorisés à voyager:

final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();

Les deux premiers exemples illustrent extends - la limite la plus étroite que vous pouvez supposer à partir de Collection est Runnable car l'utilisateur peut transmettre une Collection de tout ce qui a Runnable dans sa hiérarchie d'héritage. 

Les deux autres exemples illustrent super - la limite la plus étroite que vous pouvez supposer à partir de Collection est Object car nous autorisons tout ce qui est in la hiérarchie d'héritage de Runnable.

1
Boris the Spider

Pour la première question: vous ne pouvez pas non plus définir une méthode avec ? comme paramètre de type. Les éléments suivants ne seront pas compilés:

void <?> foo() {}

? est utilisé pour la liaison à un autre générique sans fournir le paramètre type. Vous pouvez écrire pour les méthodes:

void foo(List<?> e) {}

Et vous pouvez aussi écrire pour les cours:

public class Bar<E extends List<?>> { }

Pour l'utilisation de super

public void printAll(MyList<? super MyClass>){
    // code code code
}

Ce ne sera pas comme vous dites imprimer la liste "si elle a des objets de MyClass". Il peut avoir des objets de n'importe quelle classe qui est une sous-classe d'une classe qui est un parent de MyClass. Le compilateur ne sait pas au moment de la compilation quels sont les objets qui figureront dans la liste.

Pour vous en faire une idée, prenons un exemple simple avec la hiérarchie de classes Number. Float et Integer sont des enfants de Number. Vous pouvez écrire votre méthode comme ceci:

public void printAll(List<? super Float>){
    // code code code
}

Ensuite, vous pouvez appeler cette méthode avec un List<Number>:

List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.

Ceci est possible ne serait pas super utile dans ce cas. Ce serait utile, par exemple, lorsque vous voulez ajouter Float à une collection sans vouloir que ce soit uniquement une liste, comme:

public void addFloat(List<? super Float> list){
    list.add(2.5);
}
0
Cyrille Ka

Hmmmm votre déclaration sur super (printAll(MyList<? super MyClass>)) n'est pas claire. Ce que cela signifie, en supposant que Myclass étend Object, c'est que vous pouvez printAll(MyList<MyClass>) et vous pouvez printAll(MyList<Object>) mais rien d'autre .... cela signifie que le type générique de MyList doit être une superclasse (pas une sous-classe) de MyClass. Ceci est différent de ce que vous avez dit.

En ce qui concerne T, E, K, V ou N, eh bien, ce sont des noms sans signification en eux-mêmes. Vous pouvez utiliser ce que vous voulez. Convention suggère cependant des valeurs majuscules avec une seule lettre, et T est souvent utilisé pour les méthodes génériques, et E pour les classes ....

0
rolfl