Listeye <nasıl ekleyebilirim? Sayı> veri yapılarını genişletir?


154

Ben böyle ilan bir listesi var:

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

Foo3'e 3 eklemeye çalıştım. Ancak böyle bir hata mesajı alıyorum:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)

61
Bunun List<? extends Number>"tümü genişleyen farklı türdeki nesnelerin listesi" anlamına gelmediğini unutmayın Number. "Genişleyen tek tip nesnelerin listesi" anlamına gelir Number.
Pavel Minaev

1
PECS kuralını kontrol etsen iyi olur, Üretici genişler, Tüketici süper. stackoverflow.com/questions/2723397/…
noego

Yanıtlar:


303

Üzgünüm ama yapamazsın.

Joker karakter bildirimi List<? extends Number> foo3, değişkenin foo3bir tür ailesinden herhangi bir değeri tutabileceği anlamına gelir (belirli bir türün herhangi bir değeri yerine). Bu, bunlardan herhangi birinin yasal görevler olduğu anlamına gelir:

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

Dolayısıyla, List foo3buna göre, yukarıdaki olası ArrayListgörevlerden herhangi birinden sonra buna ne tür bir nesne ekleyebilirdiniz :

  • Bir ekleyemezsiniz Integerçünkü foo3bir işaret olabilir List<Double>.
  • Ekleyemezsiniz Doubleçünkü a foo3işaret ediyor olabilir List<Integer>.
  • Ekleyemezsiniz Numberçünkü a foo3işaret ediyor olabilir List<Integer>.

Herhangi bir nesneyi ekleyemezsiniz List<? extends T>çünkü ne tür bir nesneyi Listgerçekten gösterdiğini garanti edemezsiniz, böylece nesneye izin verildiğini garanti edemezsiniz List. Tek "garanti" sadece ondan okuyabilir ve bir Tveya alt sınıf alırsınız T.

Ters mantık super, ör List<? super T>. Bunlar yasal:

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

Belirli bir T türünü (örn. Number) Okuyamazsınız , List<? super T>çünkü Listbunun ne tür olduğuna işaret edemezsiniz . Sahip olduğunuz tek "garanti" , işaret edilen listenin bütünlüğünü ihlal etmeden Tbir tür (veya herhangi bir alt sınıfı T) değeri ekleyebilmenizdir .


Bunun mükemmel bir örneği aşağıdakilerin imzasıdır Collections.copy():

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

srcListe bildiriminin extends, ilgili Liste türlerinin bir ailesinden herhangi bir Listeyi geçirmeme ve yine de T türünün veya T alt sınıflarının değerlerini üreteceğini garanti etmem için nasıl kullandığına dikkat edin . Ancak srclisteye eklenemezsiniz .

destListesi bildirimi kullanımları superbeni ilişkili Liste türlerinin bir aileden herhangi Listesini geçmesine izin ve hala o listeye belirli tip T değerini yazabilirsiniz garanti etmek. Ama değerlerini okumak için garanti edilemez spesifik ben listeden okursanız tipi T.

Şimdi, genel joker karakterler sayesinde, bu çağrılardan herhangi birini bu tek yöntemle yapabilirim:

// 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());

Beyninizi egzersiz için bu kafa karıştırıcı ve çok geniş kodu düşünün. Yorum yapılan satırlar yasa dışıdır ve satırın en sağında belirtilme nedeni (bazılarını görmek için kaydırmanız gerekir):

  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>

11
Neden `` Liste <'' gibi bir cümle yazmamıza izin veriliyor? genişletir Number> foo3 = new ArrayList <Integer> (); `bu listeye bir öğe ekleyemezsek?
Amit Kumar Gupta

7
@articlestack: Eklemek, bir listeyle yapılacak tek yararlı şey değildir. Bir liste doldurulduktan sonra, listeden okumak faydalı olabilir. Genel joker karakterler, bir liste ailesi için genel olarak çalışan bir kod yazmanıza olanak tanır; örneğin, hem Liste <Integer> hem de Liste <Double> için çalışır. Örneğin, Collection.copy () imzasına bakın. src ListArgüman kullanır extendsiken, src listeden okumak dest Listargüman kullandığı superhedef listesine yazmak için. Bu kopyalayabilirsiniz bir yöntem verir List<Integer>veya List<Double>içine List<Number>veya List<Object>.
Bert F

Bert F, geç bir yorum için özür dilerim. Pasajınızda "T tipi bir değer (veya T'nin alt sınıfı) eklemek için" yazım hatası var mı (veya T'nin üst sınıfı) mı?
Vorteks

evet, ben de gördüm @Vortex Doğru olup olmadığından emin değilim ya da yanlış okudum.
Ungeheuer

1
@Vortex - or subclass of Tdoğrudur. Örneğin, a Object(üst sınıfı Number) ekleyemiyorum List<? super Number> foo3çünkü foo3şu şekilde atanmış olabilir: List<? super Number> foo3 = new ArrayList<Number>(yalnızca Numberveya alt sınıflarını içerebilir Number). <? super Number>, List<>atanabilecek foo3/ okunabilecek şey türlerine değil, atanabilecek türlere atıfta bulunur . Eklenebilecek / çıkarılabilecek foo3şeyler, atanabilecek her türden eklenebilecek / çıkarılabilecek şeyler olmalıdır . List<>foo3
Bert F

17

Yapamazsınız (güvenli olmayan dökümler olmadan). Sadece onlardan okuyabilirsiniz.

Sorun, listenin tam olarak ne olduğunu bilmemenizdir. Bu, herhangi bir Number alt sınıfının bir listesi olabilir, bu nedenle bir öğeyi içine koymaya çalıştığınızda, öğenin aslında listeye sığdığını bilmezsiniz.

Örneğin, Liste bir Bytes listesi olabilir , bu nedenle içine bir koymak bir hata Floatolur.


2

"Liste" <"? Sayı genişletir> aslında bir üst sınır joker!

Üst sınır joker, Number veya Number'ı genişleten herhangi bir sınıfın resmi parametre türü olarak kullanılabileceğini söylüyor: Sorun, Java'nın List'in gerçekten hangi tür olduğunu bilmediğinden kaynaklanıyor . Tam ve benzersiz bir tip olmalıdır. Umut ediyorum bu yardım eder :)


2

Burada cevapları okumama rağmen, Pavel Minaev'in yorumunu bulana kadar kafa karıştırıcı oldu:

Liste <? sayı genişletir> her biri Sayı'yı genişleten farklı türdeki nesnelerin listesi anlamına gelmez. "Numarayı genişleten tek tip nesnelerin listesi" anlamına gelir

Bundan sonra BertF'in harika açıklamasını anladım. Liste <? sayı> anlamına gelir? Number (Integer, Double, vb.) ve herhangi bir tipte olabilir ve bildirimde (Liste <? Sayı> liste genişletir ) temizlenmez ; aynı tipte ya da olmayan; tipi ne?

Liste <? sayı genişletir> yalnızca oluşturma sırasında ayarlanabilir.

Ayrıca şunu da unutmayın: Şablonları kullanırken derleyiciye hangi türle uğraştığımızı söylüyoruz. Örneğin T bizim için bu türü tutuyor, ama değil mi? aynısını yapar

Söylemeliyim .. Bu açıklamak / öğrenmek için kirli olanlardan biri


1

Bunun yerine şunları yapabilirsiniz:

  List<Number> foo3 = new ArrayList<Number>();      
  foo3.add(3);

0

Listeye farklı türde bir başvuru oluşturarak kopyalayabilirsiniz.

(Bunlar sepp2k tarafından belirtilen "güvensiz dökümlerdir").

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);

Çünkü untypedList, superclassedListve trulyclassedListsadece referans olan list, hala orijinal ArrayList unsurları ekleyerek olacaktır.

(List<?>)Yukarıdaki örnekte aslında kullanmanız gerekmez , ancak size listverilen türe bağlı olarak kodunuzda olması gerekebilir .

Bunu ?, işlevinizin üzerine getirinceye kadar kullanmanın derleyici uyarıları vereceğini unutmayın:

@SuppressWarnings("unchecked")

-1

Burada 'Object' öğesinden listeyi genişletmek için list.add komutunu kullanabilirsiniz ve list.get komutunu kullanmak istediğinizde yalnızca Object'inizin Object'e dökülmesi gerekir;

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.