Apa perbedaan antara <? extends Foo> dan <Foo>

20

Saya tampaknya memiliki kesalahpahaman tentang perbedaan antara <Foo>dan <? extends Foo>. Dari pemahaman saya, jika kita punya

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

Ini menunjukkan bahwa objek tipe Foodapat ditambahkan ke daftar array ini. Karena subclass dari Foojuga bertipe Foo, mereka juga dapat ditambahkan tanpa kesalahan, seperti yang diilustrasikan oleh

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

mana Bar extends Foo.

Sekarang, katakanlah saya telah mendefinisikan foossebagai

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

Pemahaman saya saat ini adalah bahwa ini diungkapkan some unknown type that extends Foo. Saya menganggap ini berarti bahwa objek apa pun yang merupakan subkelas Foodapat ditambahkan ke daftar ini; artinya tidak ada perbedaan antara ArrayList<Foo>dan ArrayList<? extends Foo>.

Untuk menguji ini, saya mencoba menulis kode berikut

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

tetapi diminta dengan kesalahan kompilasi berikut

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)

Berdasarkan pemahaman saya saat ini, saya dapat melihat mengapa saya mungkin tidak dapat menambahkan a Fooke daftar <? extends Foo>, karena itu bukan subkelas dari dirinya sendiri; tapi saya ingin tahu mengapa saya tidak bisa menambahkan Barke daftar.

Di mana lubang pemahaman saya?

Zymus
sumber
1
<? extends Foo>adalah kelas spesifik dan tidak dikenal yang diperluas Foo. Operasi dengan kelas ini hanya sah jika akan sah untuk setiap subkelas dari Foo.
Ordous
3
Wow. Semakin banyak Anda belajar tentang obat generik Java, semakin banyak kekacauan yang Anda temukan.
Mason Wheeler

Jawaban:

15

Seperti yang Anda temukan sendiri, ArrayListpernyataan ArrayList<? extends Foo> subFoos = new ArrayList<>();tidak akan sangat berguna.

Untuk melihat manfaat dari <? extends T>pertimbangan ini:

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

yang nantinya bisa digunakan sebagai berikut:

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

atau sebagai berikut:

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

perhatikan bahwa tidak satu pun di atas akan berfungsi jika collectmetode telah dinyatakan sebagai berikut:

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