web-dev-qa-db-fra.com

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

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 Foo peuvent être ajoutés à cette liste de tableaux. Comme les sous-classes de Foo sont é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 foos comme

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

Ma compréhension actuelle est que cela exprime some unknown type that extends Foo. Je suppose que cela signifie que tous les objets qui sont une sous-classe de Foo peuvent ê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)

D'après ma compréhension actuelle, je peux voir pourquoi je ne pourrais peut-être pas 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?

23
Zymus

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

Afin de voir l'utilité de <? extends T> considère 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>() );

notez que rien de ce qui précède n'aurait fonctionné si la méthode collect avait été déclarée comme suit:

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