Bagaimana elemen dapat ditambahkan ke koleksi generik wildcard?

Mengapa saya mendapatkan kesalahan kompiler dengan kode Java ini?

1  public List<? extends Foo> getFoos()
2  {
3    List<? extends Foo> foos = new ArrayList<? extends Foo>();
4    foos.add(new SubFoo());
5    return foos;
6  }

Dimana 'SubFoo' adalah kelas konkret yang mengimplementasikan Foo, dan Foo adalah sebuah antarmuka.

Kesalahan yang saya dapatkan dengan kode ini:

  • Di Baris 3: "Tidak dapat membuat instance ArrayList‹? extends Foo›"
  • Pada Baris 4: "Metode add(capture#1-of ? extends Foo) dalam tipe List‹capture#1-of ? extends Foo› tidak berlaku untuk argumen (SubFoo)"

Pembaruan: Terima kasih kepada Jeff C, saya dapat mengubah Baris 3 menjadi "new ArrayList‹Foo›();". Tapi saya masih mengalami masalah dengan Jalur 4.


person David Koelle    schedule 06.10.2008    source sumber


Jawaban (5)


Gunakan ini sebagai gantinya:

1  public List<? extends Foo> getFoos()
2  {
3    List<Foo> foos = new ArrayList<Foo>(); /* Or List<SubFoo> */
4    foos.add(new SubFoo());
5    return foos;
6  }

Setelah Anda mendeklarasikan foos sebagai List<? extends Foo>, kompiler tidak mengetahui bahwa aman untuk menambahkan SubFoo. Bagaimana jika ArrayList<AltFoo> telah ditugaskan ke foos? Itu akan menjadi penugasan yang valid, tetapi menambahkan SubFoo akan mencemari koleksi.

person erickson    schedule 06.10.2008

Saya pikir saya akan menambahkan ke utas lama ini, dengan merangkum properti parameter Daftar yang dipakai dengan tipe atau wildcard....

Ketika suatu metode memiliki parameter/hasil berupa Daftar, penggunaan tipe instantiasi atau wildcard menentukan

  1. Tipe Daftar yang dapat diteruskan ke metode sebagai argumen
  2. Jenis Daftar yang dapat diisi dari hasil metode
  3. Jenis elemen yang dapat ditulis ke daftar dalam metode
  4. Tipe yang dapat diisi ketika membaca elemen dari daftar dalam metode

Param/Jenis pengembalian: List< Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
  2. Types of List which can be populated from the method result:
    • List< Foo>
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
  3. Types of elements which can be written to list within the method:
    • Foo & subtypes
  4. Types which can be populated when reading elements from list within the method:
    • Foo & supertypes (up to Object)

Param/Jenis pengembalian: List< ? extends Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
    • List< Subfoo>
    • List< SubSubFoo>
    • List< ? extends Foo>
    • List< ? extends SubFoo>
    • List< ? extends SubSubFoo>
  2. Types of List which can be populated from the method result:
    • List< ? extends Foo>
    • List< ? extends SuperFoo>
    • List< ? extends SuperSuperFoo>
  3. Types of elements which can be written to list within the method:
    • None! Not possible to add.
  4. Types which can be populated when reading elements from list within the method:
    • Foo & supertypes (up to Object)

Param/Jenis pengembalian: List<? super Foo>

  1. Types of List which can be passed to the method as an argument:
    • List< Foo>
    • List< Superfoo>
    • List< SuperSuperFoo>
    • List< ? super Foo>
    • List< ? super SuperFoo>
    • List< ? super SuperSuperFoo>
  2. Types of List which can be populated from the method result:
    • List< ? super Foo>
    • List< ? super SubFoo>
    • List< ? super SubSubFoo>
  3. Types of elements which can be written to list within the method:
    • Foo & supertypes
  4. Types which can be populated when reading elements from list within the method:
    • Foo & supertypes (up to Object)

Interpretasi/Komentar

  • kebutuhan penelepon eksternal mendorong desain deklarasi metode yaitu API publik (biasanya pertimbangan utama)
  • kebutuhan logika metode internal mendorong keputusan tambahan apa pun mengenai tipe data aktual yang dideklarasikan dan dibangun secara internal (biasanya pertimbangan sekunder)
  • gunakan List<Foo> jika kode pemanggil selalu fokus pada manipulasi kelas Foo, karena ini memaksimalkan fleksibilitas untuk membaca dan menulis
  • gunakan List<? extends UpperMostFoo> jika ada banyak tipe pemanggil yang berbeda, fokus pada manipulasi kelas yang berbeda (tidak selalu Foo) dan ada satu kelas paling atas dalam hierarki tipe Foo, dan jika metodenya adalah menulis secara internal ke daftar dan daftar pemanggil manipulasi adalah membaca. Di sini metode ini mungkin menggunakan List< UpperMostFoo> secara internal dan menambahkan elemen ke dalamnya, sebelum mengembalikan List< ? extends UpperMostFoo>
  • jika ada banyak tipe pemanggil yang berbeda, fokus pada manipulasi kelas yang berbeda (tidak selalu Foo) dan jika membaca dan menulis ke daftar diperlukan dan ada satu kelas terendah dalam hierarki tipe Foo, maka masuk akal untuk menggunakan List< ? super LowerMostFoo>
person Glen Best    schedule 16.07.2012
comment
Menurut saya 3 dan 4 untuk List<? super Foo> salah. Itu harus Foo & subtipe dan Object masing-masing. - person FuegoFro; 22.08.2016

Mencoba:

public List<Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}

Konstruktor ArrayList generik harus memiliki tipe tertentu untuk dijadikan parameter, Anda tidak dapat menggunakan '?' karakter pengganti di sana. Mengubah instantiasi menjadi "new ArrayList‹Foo›()' akan menyelesaikan kesalahan kompilasi pertama.

Deklarasi variabel 'foos' dapat memiliki wildcard, namun karena Anda mengetahui tipe persisnya, akan lebih masuk akal untuk mereferensikan info tipe yang sama di sana. Apa yang Anda miliki sekarang mengatakan bahwa foos memiliki beberapa subtipe spesifik dari Foo, tetapi kami tidak tahu yang mana. Menambahkan SubFoo mungkin tidak diperbolehkan, karena SubFoo bukan "semua subtipe Foo". Mengubah deklarasi menjadi 'List‹Foo› foos = ' memecahkan kesalahan kompilasi kedua.

Terakhir, saya akan mengubah tipe pengembalian menjadi 'List‹Foo›' karena klien metode ini tidak akan dapat berbuat banyak dengan nilai yang dikembalikan seperti yang ditentukan saat ini. Anda sebaiknya jarang menggunakan wildcard dalam tipe pengembalian. Gunakan tanda tangan metode berparameter jika diperlukan, tetapi lebih memilih tipe terikat untuk hanya muncul dalam argumen metode, karena hal ini menyerahkan kepada pemanggil yang dapat meneruskan tipe tertentu dan mengoperasikannya sesuai dengan itu.

person Community    schedule 06.10.2008
comment
Saya tidak setuju dengan poin terakhir Anda. Tipe pengembalian wildcard sangat berguna jika pemanggil hanya perlu mengeluarkan sesuatu dari daftar. - person newacct; 16.07.2012

Berikut ini akan berfungsi dengan baik:

public List<? extends Foo> getFoos() {
    List<Foo> foos = new ArrayList<Foo>();
    foos.add(new SubFoo());
    return foos;
}
person SCdF    schedule 06.10.2008

Untuk mendapatkan gambaran tentang cara kerja obat generik, lihat contoh ini:

    List<SubFoo> sfoo = new ArrayList<SubFoo>();
    List<Foo> foo;
    List<? extends Foo> tmp;

    tmp = sfoo;
    foo = (List<Foo>) tmp;

Masalahnya, itu tidak dirancang untuk variabel lokal/anggota, tetapi untuk tanda tangan fungsi, itulah mengapa ini sangat terbalik.

person zslevi    schedule 23.03.2011