Bagaimana saya bisa menambahkan ke Daftar <? extends Number> struktur data?

154

Saya memiliki Daftar yang dinyatakan seperti ini:

 List<? extends Number> foo3 = new ArrayList<Integer>();

Saya mencoba menambahkan 3 ke foo3. Namun saya mendapatkan pesan kesalahan seperti ini:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
unj2
sumber
61
Catatan itu List<? extends Number>tidak berarti "daftar objek dari berbagai jenis, yang semuanya meluas Number". Ini berarti "daftar objek dari tipe tunggal yang memanjang Number".
Pavel Minaev
1
Anda lebih baik memeriksa aturan Pecs, ekstensi Produsen, Super konsumen. stackoverflow.com/questions/2723397/…
noego

Jawaban:

303

Maaf, tetapi Anda tidak bisa.

Deklarasi wildcard List<? extends Number> foo3berarti bahwa variabel foo3dapat menyimpan nilai apa pun dari keluarga jenis (bukan nilai apa pun dari jenis tertentu). Ini berarti bahwa semua ini adalah penugasan legal:

List<? extends Number> foo3 = new ArrayList<Number>;  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>;  // Double extends Number

Jadi, mengingat ini, jenis objek apa yang bisa Anda tambahkan List foo3yang akan sah setelah salah satu dari ArrayListpenugasan di atas :

  • Anda tidak dapat menambahkan Integerkarena foo3bisa menunjuk sebuah List<Double>.
  • Anda tidak dapat menambahkan Doublekarena foo3bisa menunjuk pada a List<Integer>.
  • Anda tidak dapat menambahkan Numberkarena foo3bisa menunjuk pada a List<Integer>.

Anda tidak dapat menambahkan objek apa pun List<? extends T>karena Anda tidak dapat menjamin jenis apaList yang benar-benar mengarah, sehingga Anda tidak dapat menjamin bahwa objek tersebut diizinkan List. Satu-satunya "jaminan" adalah bahwa Anda hanya dapat membacanya dan Anda akan mendapatkan Tatau subkelas dari T.

Logika balik berlaku untuk super, misalnyaList<? super T> . Ini legal:

List<? super Number> foo3 = new ArrayList<Number>; // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>; // Object is a "super" of Number

Anda tidak dapat membaca jenis T tertentu (misalnya Number) List<? super T>karena Anda tidak dapat menjamin jenis apa Listyang benar-benar mengarah. Satu-satunya "jaminan" yang Anda miliki adalah Anda dapat menambahkan nilai tipeT (atau subkelas apa pun T) tanpa melanggar integritas daftar yang ditunjuk.


Contoh sempurna dari ini adalah tanda tangan untuk Collections.copy() :

public static <T> void copy(List<? super T> dest,List<? extends T> src)

Perhatikan bagaimana src deklarasi daftar digunakan extendsuntuk memungkinkan saya untuk melewati Daftar apa pun dari keluarga tipe Daftar terkait dan masih menjamin itu akan menghasilkan nilai tipe T atau subkelas dari T. Tapi Anda tidak dapat menambahkan ke srcdaftar.

The destpenggunaan daftar deklarasi superuntuk mengizinkan saya untuk lulus Daftar apapun dari keluarga jenis Daftar terkait dan masih menjamin aku bisa menulis nilai dari jenis tertentu T ke daftar itu. Tetapi tidak dapat dijamin untuk membaca nilai-nilai tipe T tertentu jika saya membaca dari daftar.

Jadi sekarang, berkat wildcard generik, saya dapat melakukan semua panggilan ini dengan metode tunggal:

// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double());

Pertimbangkan kode yang membingungkan dan sangat luas ini untuk melatih otak Anda. Baris yang dikomentari adalah ilegal dan alasan mengapa dinyatakan pada ujung kanan garis (perlu menggulir untuk melihat beberapa di antaranya):

  List<Number> listNumber_ListNumber  = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>();                    // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble  = new ArrayList<Double>();                     // error - can assign only exactly <Number>

  List<? extends Number> listExtendsNumber_ListNumber  = new ArrayList<Number>();
  List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
  List<? extends Number> listExtendsNumber_ListDouble  = new ArrayList<Double>();

  List<? super Number> listSuperNumber_ListNumber  = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();      // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble  = new ArrayList<Double>();       // error - Double is not superclass of Number


//List<Integer> listInteger_ListNumber  = new ArrayList<Number>();                  // error - can assign only exactly <Integer>
  List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble  = new ArrayList<Double>();                  // error - can assign only exactly <Integer>

//List<? extends Integer> listExtendsInteger_ListNumber  = new ArrayList<Number>(); // error - Number is not a subclass of Integer
  List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble  = new ArrayList<Double>(); // error - Double is not a subclass of Integer

  List<? super Integer> listSuperInteger_ListNumber  = new ArrayList<Number>();
  List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble  = new ArrayList<Double>();     // error - Double is not a superclass of Integer


  listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List<Number>

  // These next 3 are compile errors for the same reason:
  // You don't know what kind of List<T> is really
  // being referenced - it may not be able to hold an Integer.
  // You can't add anything (not Object, Number, Integer,
  // nor Double) to List<? extends Number>      
//listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>

  listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List<Number> or List<Object>

  listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List<Integer> (duh)

  // This fails for same reason above - you can't
  // guarantee what kind of List the var is really
  // pointing to
//listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List<X> that is only allowed to hold X's

  listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
  listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
Bert F
sumber
11
Mengapa kita diizinkan menulis kalimat seperti `Daftar <? extends Number> foo3 = new ArrayList <Integer> (); `jika kita tidak dapat menambahkan elemen ke daftar ini?
Amit Kumar Gupta
7
@articlestack: Menambahkan bukan satu-satunya hal yang berguna untuk dilakukan dengan daftar. Setelah daftar diisi, membaca dari daftar mungkin berguna. Wildcards umum memungkinkan penulisan kode yang berfungsi secara umum untuk sekumpulan daftar, mis. Bekerja untuk Daftar <Integer> atau Daftar <Double>. Misalnya, lihat tanda tangan dari Collection.copy (). The src Listpenggunaan argumen extendsuntuk membaca dari daftar src, sedangkan yang dest Listmenggunakan argumen superuntuk menulis ke daftar dest. Ini memungkinkan satu metode yang dapat menyalin dari List<Integer>atau List<Double>ke List<Number>atau List<Object>.
Bert F
Bert F, maaf atas komentar yang terlambat. Apakah ada kesalahan ketik pada bagian Anda, "untuk menambahkan nilai tipe T (atau subkelas T)" harus membaca (atau superclass T)?
Vortex
ya, saya melihatnya juga @Vortex Saya tidak yakin apakah itu benar atau saya membacanya salah.
Ungeheuer
1
@Vortex - or subclass of Tbenar. Sebagai contoh, saya tidak dapat menambahkan sebuah Object(superclass Number) ke List<? super Number> foo3karena foo3mungkin telah ditetapkan sebagai: List<? super Number> foo3 = new ArrayList<Number>(yang dapat berisi hanya Numberatau subkelas dari Number). <? super Number>mengacu pada jenis List<>s yang dapat ditugaskan foo3- bukan jenis hal yang dapat ditambahkan / dibaca dari itu. Jenis-jenis hal yang dapat ditambahkan / dihapus foo3harus berupa hal-hal yang dapat ditambahkan / dihapus dari segala jenis List<>yang dapat ditugaskan foo3.
Bert F
17

Anda tidak bisa (tanpa gips yang tidak aman). Anda hanya dapat membaca dari mereka.

Masalahnya adalah Anda tidak tahu daftar itu. Itu bisa berupa daftar subkelas Number, jadi ketika Anda mencoba memasukkan elemen ke dalamnya, Anda tidak tahu bahwa elemen tersebut benar-benar cocok dengan daftar.

Misalnya Daftar mungkin daftar Bytes, jadi itu akan menjadi kesalahan untuk memasukkannya Floatke dalamnya.

sepp2k
sumber
2

"Daftar '<'? Extends Number> sebenarnya adalah wildcard batas atas!

Wildcard batas atas mengatakan bahwa setiap kelas yang memperluas Angka atau Angka itu sendiri dapat digunakan sebagai tipe parameter formal: Masalahnya berasal dari fakta bahwa Java tidak tahu apa tipe Daftar sebenarnya. Itu harus menjadi tipe EXACT dan UNIK. Saya harap ini membantu :)

andygro
sumber
2

Itu membingungkan saya walaupun saya membaca jawaban di sini, sampai saya menemukan komentar oleh Pavel Minaev:

Perhatikan Daftar itu <? extends Number> tidak berarti "daftar objek dari tipe yang berbeda, yang semuanya memperpanjang Number". Ini berarti "daftar objek dari tipe tunggal yang meluas Angka"

Setelah ini saya bisa mengerti penjelasan BertF yang luar biasa. Daftar <? extends Number> means? bisa dari jenis apa saja yang memperpanjang Nomor (Integer, Double, dll) dan tidak diklarifikasi dalam deklarasi (Daftar <? extends Number> daftar) yang mana dari mereka itu, jadi ketika Anda ingin menggunakan metode add itu tidak diketahui jika inputnya adalah dari jenis yang sama atau tidak; apa jenisnya?

Jadi elemen Daftar <? extends Number> hanya dapat diatur saat membuat.

Perhatikan juga ini: Ketika kita menggunakan templat, kita memberi tahu kompiler jenis apa yang kita mainkan. T misalnya memegang jenis itu untuk kita, tetapi tidak ?melakukan hal yang sama

Saya harus mengatakan .. Ini adalah salah satu yang kotor untuk dijelaskan / dipelajari

navid
sumber
1

Anda bisa melakukan ini sebagai gantinya:

  List<Number> foo3 = new ArrayList<Number>();      
  foo3.add(3);
Ryan Elkins
sumber
0

Anda dapat memperdayainya dengan membuat referensi ke Daftar dengan tipe yang berbeda.

(Ini adalah "pemain tidak aman" yang disebutkan oleh sepp2k.)

List<? extends Number> list = new ArrayList<Integer>();

// This will not compile
//list.add(100);

// WORKS, BUT NOT IDEAL
List untypedList = (List)list;
// It will let you add a number
untypedList.add(200);
// But it will also let you add a String!  BAD!
untypedList.add("foo");

// YOU PROBABLY WANT THIS
// This is safer, because it will (partially) check the type of anything you add
List<Number> superclassedList = (List<Number>)(List<?>)list;
// It will let you add an integer
superclassedList.add(200);
// It won't let you add a String
//superclassedList.add("foo");
// But it will let you add a Float, which isn't really correct
superclassedList.add(3.141);
// ********************
// So you are responsible for ensuring you only add/set Integers when you have
// been given an ArrayList<Integer>
// ********************

// EVEN BETTER
// If you can, if you know the type, then use List<Integer> instead of List<Number>
List<Integer> trulyclassedList = (List<Integer>)(List<?>)list;
// That will prevent you from adding a Float
//trulyclassedList.add(3.141);

System.out.println("list: " + list);

Karena untypedList, superclassedListdan trulyclassedListhanya referensilist , Anda masih akan menambahkan elemen ke ArrayList asli.

Anda sebenarnya tidak perlu menggunakan (List<?>)dalam contoh di atas, tetapi Anda mungkin membutuhkannya dalam kode Anda, tergantung pada jenislist Anda diberikan.

Perhatikan bahwa menggunakan ?akan memberi Anda peringatan kompiler, sampai Anda menempatkan ini di atas fungsi Anda:

@SuppressWarnings("unchecked")
joeytwiddle
sumber
-1

tempat memperluas daftar dari 'Objek', Anda dapat menggunakan list.add dan ketika ingin menggunakan list.get hanya perlu melemparkan Objek ke Objek Anda;

abbasalim
sumber