2010-09-15 5 views

Répondre

36

C'est fait par souci de sécurité. Imaginez si cela a fonctionné:

List<Child> childList = new ArrayList<Child>(); 
childList.add(new Child()); 

List<? extends Parent> parentList = childList; 
parentList.set(0, new Parent()); 

Child child = childList.get(0); // No! It's not a child! Type safety is broken... 

Le sens de List<? extends Parent> est « La est une liste d'un certain type qui étend Parent Nous ne savons pas quel type - il pourrait être un List<Parent>, un List<Child> ou un List<GrandChild>.. " Cela rend-il sûr de chercher des articles sur de l'API List<T> et convertir T-Parent, mais ce n'est pas sûr d'appeler en à l'API List<T> conversion Parent-T ... parce que la conversion peut être invalide.

+0

Mauvais exemple de polymorphisme, je pense? Un "enfant" n'est pas (toujours) un "parent". – justhalf

+0

@justhalf: 'Parent' était dans la question, et suggère que' Child' est un exemple naturel d'une sous-classe. Pas les noms de classe que j'aurais choisis à partir de rien, mais assez clair en termes de contexte de la question. –

+0

Ouais, je comprends, mais je pense qu'il aurait été préférable que l'exemple utilise ici "SingleParent" ou "NewParent" comme sous-classe =) – justhalf

16
List<? super Parent> 

PECS - «Producteur - Extends, Cosumer - Super». Votre List est un consommateur de Parent objets.

1

Voici ma compréhension.

Supposons que nous ayons un type générique avec 2 méthodes

type L<T> 
    T get(); 
    void set(T); 

Supposons que nous avons un type super P, et il a sous-types C1, C2 ... Cn. (Pour plus de commodité, nous disons P est un sous-type de lui-même, et est en fait l'un des Ci)

Maintenant, nous avons également obtenu n types de béton L<C1>, L<C2> ... L<Cn>, comme si nous avons écrit manuellement n types:

type L_Ci_ 
    Ci get(); 
    void set(Ci); 

Nous n'avons pas eu à les écrire manuellement, c'est le point. Il n'y a pas les relations entre ces types

L<Ci> oi = ...; 
L<Cj> oj = oi; // doesn't compile. L<Ci> and L<Cj> are not compatible types. 

Pour le modèle de C, qui est la fin de l'histoire. Il s'agit essentiellement d'une macro-expansion - basée sur une classe "template", elle génère de nombreuses classes concrètes, sans aucune relation de type entre elles.

Pour Java, il y a plus. Nous avons également obtenu un type L<? extends P>, il est un type super de tout L<Ci>

L<Ci> oi = ...; 
L<? extends P> o = oi; // ok, assign subtype to supertype 

Quel genre de méthode devrait exister dans L<? extends P>? En tant que type super, n'importe laquelle de ses méthodes doit être cornée par ses sous-types. Cette méthode fonctionnerait:

type L<? extends P> 
    P get(); 

parce que dans l'un de ses sous-type L<Ci>, il y a une méthode Ci get(), qui est compatible avec P get() - la méthode redéfinie a la même signature et le type de retour covariant.

Cela peut ne pas fonctionner pour set() bien - nous ne pouvons pas trouver un type X, de sorte que void set(X) peut être surchargée par void set(Ci) pour tout Ci. Par conséquent, la méthode set() n'existe pas dans L<? extends P>. Il y a également un L<? super P> qui va dans l'autre sens. Il a set(P), mais pas get(). Si Si est un super type de P, L<? super P> est un super type de L<Si>.

type L<? super P> 
    void set(P); 

type L<Si> 
    Si get(); 
    void set(Si); 

set(Si) « » set(P) pas overrides au sens habituel, mais le compilateur peut voir que toute invocation valable sur set(P) est une invocation valide sur set(Si)

+0

Une grande explication sur la raison set() est refusée dans ce contexte. –

Questions connexes