Яка різниця між <? розширює Foo> і <Foo>


20

У мене, здається, є непорозуміння щодо різниці між <Foo>і <? extends Foo>. З мого розуміння, якби ми мали

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

Це вказує на те, що об’єкти типу Fooможуть бути додані до цього списку масивів. Оскільки підкласи типу Fooтакож є типовими Foo, їх можна також додавати без помилок, як це проілюстровано у

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

де Bar extends Foo.

Тепер, скажімо, я визначив foosяк

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

Моє теперішнє розуміння, що це виражає some unknown type that extends Foo. Я вважаю це тим, що Fooдо цього списку можуть бути додані будь-які об'єкти, що є підкласом ; тобто немає різниці між ArrayList<Foo>і ArrayList<? extends Foo>.

Щоб перевірити це, я спробував написати наступний код

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

але було запропоновано наступну помилку компіляції

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)

Виходячи з мого теперішнього розуміння, я бачу, чому я не можу додати Fooсписок до списку <? extends Foo>, оскільки він не є самим підкласом; але мені цікаво, чому я не можу додати Barдо списку.

Де дірка в моєму розумінні?



1
<? extends Foo>це специфічний і невідомий клас, який розширюється Foo. Операція з цим класом є законною лише в тому випадку, якщо вона була б законною для будь-якого підкласу Foo.
Звичайний

3
Ого. Чим більше ви дізнаєтесь про дженерики Java, тим більше будете накручувати.
Мейсон Уілер

Відповіді:


15

Як ви самі виявили, ArrayListдекларація як ArrayList<? extends Foo> subFoos = new ArrayList<>();не дуже корисна.

Щоб побачити корисність, <? extends T>врахуйте це:

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

які згодом можна використовувати наступним чином:

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

або наступним чином:

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

зауважте, що жодне з вищезазначеного не працювало, якби collectметод був оголошений таким чином:

List<Foo> collect( List<Foo> a1, List<Foo> a2 )
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.