Was ist der Unterschied zwischen <? erweitert Foo> und <Foo>

20

Ich habe offenbar ein Missverständnis über den Unterschied zwischen <Foo>und <? extends Foo>. Nach meinem Verständnis, wenn wir hatten

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

Dies zeigt an, dass Foodieser Array-Liste Objekte vom Typ hinzugefügt werden können. Da Unterklassen von Fooebenfalls vom Typ sind Foo, können sie auch fehlerfrei hinzugefügt werden, wie durch dargestellt

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

wo Bar extends Foo.

Sagen wir, ich hätte definiert foosals

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

Mein gegenwärtiges Verständnis ist, dass dies ausdrückt some unknown type that extends Foo. Damit meine ich, dass alle Objekte, die zu einer Unterklasse gehören Foo, zu dieser Liste hinzugefügt werden können. Das heißt, es gibt keinen Unterschied zwischen ArrayList<Foo>und ArrayList<? extends Foo>.

Um dies zu testen, habe ich versucht, den folgenden Code zu schreiben

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

wurde jedoch mit dem folgenden Kompilierungsfehler aufgefordert

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)

Nach meinem derzeitigen Verständnis kann ich sehen, warum ich möglicherweise kein zu Fooeiner Liste von hinzufügen kann <? extends Foo>, weil es keine Unterklasse von sich selbst ist. aber ich bin gespannt, warum ich a nicht hinzufügen kannBar der Liste .

Wo ist das Loch in meinem Verständnis?

Zymus
quelle
1
<? extends Foo>ist eine spezifische und unbekannte Klasse, die sich erweitert Foo. Eine Operation mit dieser Klasse ist nur dann zulässig, wenn sie für eine Unterklasse von zulässig wäre Foo.
Ordentliche
3
Wow. Je mehr Sie über die Generika von Java erfahren, desto schlechter wird es.
Mason Wheeler

Antworten:

15

Wie Sie selbst festgestellt haben, wäre ein ArrayListdeklariertes als ArrayList<? extends Foo> subFoos = new ArrayList<>();nicht sehr nützlich.

Um den Nutzen von zu sehen, <? extends T>überlegen Sie Folgendes:

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

die später wie folgt verwendet werden können:

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

oder wie folgt:

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

Beachten Sie, dass keines der oben genannten collectVerfahren funktioniert hätte, wenn die Methode wie folgt deklariert worden wäre:

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