Quelle est la difference entre <? étend Foo> et <Foo>

20

Je semble avoir un malentendu sur la différence entre <Foo>et <? extends Foo>. D'après ma compréhension, si nous avions

ArrayList<Foo> foos = new ArrayList<>();

Cela indique que des objets de type Foopeuvent être ajoutés à cette liste de tableaux. Comme les sous-classes de Foosont également de type Foo, elles peuvent également être ajoutées sans erreur, comme illustré par

ArrayList<Foo> foos = new ArrayList<>();
foos.add(new Foo());
foos.add(new Bar());

Bar extends Foo.

Maintenant, disons que j'avais défini fooscomme

ArrayList<? extends Foo> foos = new ArrayList<>();

Ma compréhension actuelle est que cela s'exprime some unknown type that extends Foo. Je suppose que cela signifie que tous les objets qui sont une sous-classe de Foopeuvent être ajoutés à cette liste; ce qui signifie qu'il n'y a pas de différence entre ArrayList<Foo>et ArrayList<? extends Foo>.

Pour tester cela, j'ai essayé d'écrire le code suivant

ArrayList<? extends Foo> subFoos = new ArrayList<>();
subFoos.add(new Foo());
subFoos.add(new Bar());

mais a été invité avec l'erreur de compilation suivante

no suitable method found for add(Foo)
method java.util.Collection.add(capture#1 of ? extends Foo) is not applicable
(argument mismatch; Foo cannot be converted to capture#1 of ? extends Foo)

no suitable method found for add(Bar)
method java.util.Collection.add(capture#2 of ? extends Bar) is not applicable
(argument mismatch; Bar cannot be converted to capture#2 of ? extends Bar)

Sur la base de ma compréhension actuelle, je peux voir pourquoi je ne pourrais pas être en mesure d'ajouter un Fooà une liste de <? extends Foo>, car ce n'est pas une sous-classe en soi; mais je suis curieux de savoir pourquoi je ne peux pas ajouter un Barà la liste.

Où est le trou dans ma compréhension?

Zymus
la source
1
<? extends Foo>est une classe spécifique et inconnue qui s'étend Foo. Une opération avec cette classe n'est légale que si elle est légale pour n'importe quelle sous-classe de Foo.
Ordous
3
Sensationnel. Plus vous en apprendrez sur les génériques de Java, plus vous trouverez de vissage.
Mason Wheeler

Réponses:

15

Comme vous l'avez découvert par vous-même, un ArrayListas déclaré ArrayList<? extends Foo> subFoos = new ArrayList<>();ne serait pas très utile.

Afin de voir l'utilité de <? extends T>considérer ceci:

List<Foo> collect( List<? extends Foo> a1, List<? extends Foo> a2 )
{
    List<Foo> collected = new ArrayList<>();
    collected.addAll( a1 );
    collected.addAll( a2 );
    return collected;
}

qui peut ensuite être utilisé comme suit:

List<Foo> foos = collect( new ArrayList<Foo>(), new ArrayList<Bar>() );

ou comme suit:

List<Foo> foos = collect( new ArrayList<Bar>(), new ArrayList<Foo>() );

noter qu'aucune de ces réponses n'aurait fonctionné si la collectméthode avait été déclarée comme suit:

List<Foo> collect( List<Foo> a1, List<Foo> a2 )
Mike Nakis
la source