Come posso aggiungere all'elenco <? estende Numero> strutture dati?


155

Ho un elenco che è dichiarato così:

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

Ho provato ad aggiungere 3 a foo3. Tuttavia ricevo un messaggio di errore come questo:

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
Si noti che List<? extends Number>non significa "elenco di oggetti di tipi diversi, che si estendono tutti Number". Significa "elenco di oggetti di un solo tipo che si estende Number".
Pavel Minaev,

1
È meglio controllare la regola PECS, il produttore si estende, il consumatore super. stackoverflow.com/questions/2723397/...
Noego

Risposte:


304

Scusa, ma non puoi.

La dichiarazione jolly List<? extends Number> foo3indica che la variabile foo3può contenere qualsiasi valore da una famiglia di tipi (anziché qualsiasi valore di un tipo specifico). Significa che uno di questi è incarichi legali:

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

Quindi, dato questo, quale tipo di oggetto potresti aggiungere a List foo3quello sarebbe legale dopo una qualsiasi delle ArrayListassegnazioni sopra possibili :

  • Non puoi aggiungere un Integerperché foo3potrebbe puntare a List<Double>.
  • Non puoi aggiungere un Doubleperché foo3potrebbe puntare a List<Integer>.
  • Non puoi aggiungere un Numberperché foo3potrebbe puntare a List<Integer>.

Non è possibile aggiungere alcun oggetto a List<? extends T>perché non è possibile garantire a quale tipo di Listpuntamento si sta realmente puntando, quindi non è possibile garantire che l'oggetto sia autorizzato in questo List. L'unica "garanzia" è che puoi solo leggere da esso e otterrai una To sottoclasse di T.

La logica inversa si applica super, ad es List<? super T>. Questi sono legali:

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

Non è possibile leggere il tipo specifico T (ad es. Number) List<? super T>Perché non è possibile garantire a quale tipo di Listcodice sia realmente indicato. L'unica "garanzia" che hai è che sei in grado di aggiungere un valore di tipo T(o qualsiasi sottoclasse di T) senza violare l'integrità dell'elenco indicato.


L'esempio perfetto di ciò è la firma per Collections.copy():

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

Notate come la srcdichiarazione dell'elenco utilizza extendsper permettermi di passare qualsiasi Elenco da una famiglia di tipi di Elenco correlati e garantire comunque che produrrà valori di tipo T o sottoclassi di T. Ma non è possibile aggiungere srcall'elenco.

La destdichiarazione dell'elenco superconsente di passare qualsiasi Elenco da una famiglia di tipi di Elenco correlati e garantisce comunque che posso scrivere un valore di un tipo specifico T in tale elenco. Ma non è possibile garantire la lettura dei valori di tipo specifico T se leggo dall'elenco.

Quindi ora, grazie ai caratteri jolly generici, posso fare una qualsiasi di queste chiamate con quel singolo metodo:

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

Considera questo codice confuso e molto ampio per esercitare il tuo cervello. Le righe commentate sono illegali e il motivo per cui è indicato all'estrema destra della riga (è necessario scorrere per vederne alcune):

  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
Perché ci è permesso scrivere una frase come `Elenco <? estende il numero> foo3 = new ArrayList <Integer> (); `se non possiamo più un elemento in questo elenco?
Amit Kumar Gupta,

8
@articlestack: l'aggiunta non è l'unica cosa utile da fare con un elenco. Una volta popolato un elenco, può essere utile leggere da un elenco. I caratteri jolly generici consentono di scrivere codice che funziona in modo generico per una famiglia di elenchi, ad esempio funziona sia per Elenco <Integer> che per Elenco <Doppio>. Ad esempio, guarda la firma di Collection.copy (). L' src Listargomento utilizza extendsper leggere dall'elenco src, mentre l' dest Listargomento utilizza superper scrivere nell'elenco dest. Ciò consente un metodo che può copiare da List<Integer>o List<Double>in List<Number>o List<Object>.
Bert F,

Bert F, scusa per un commento in ritardo. C'è un refuso nel tuo passaggio, "per aggiungere un valore di tipo T (o sottoclasse di T)" dovrebbe essere letto (o superclasse di T)?
Vortice

sì, l'ho visto anche io @Vortex Non sono sicuro che sia giusto o l'ho letto male.
Ungeheuer,

2
@Vortex - or subclass of Tè corretto. Ad esempio, non posso aggiungere una Object(superclasse di Number) a List<? super Number> foo3perché foo3potrebbe essere stata assegnata come: List<? super Number> foo3 = new ArrayList<Number>(che può contenere solo Numbero sottoclassi di Number). <? super Number>si riferisce ai tipi di List<>s che possono essere assegnati foo3- non ai tipi di cose che possono essere aggiunti / letti da esso. I tipi di cose che possono essere aggiunti / rimossi foo3devono essere cose che possono essere aggiunte / rimosse da qualsiasi tipo di attività List<>assegnabile foo3.
Bert F,

18

Non puoi (senza cast non sicuri). Puoi solo leggere da loro.

Il problema è che non sai di cosa sia esattamente l'elenco. Potrebbe essere un elenco di qualsiasi sottoclasse di Numero, quindi quando si tenta di inserire un elemento in esso, non si sa che l'elemento si adatta effettivamente all'elenco.

Ad esempio, l'Elenco potrebbe essere un elenco di Bytes, quindi sarebbe un errore inserirne uno Float.


2

"Elenco '<'? Estende il numero> è in realtà un carattere jolly con limite superiore!

Il carattere jolly con limite superiore indica che qualsiasi classe che estende il numero o il numero stesso può essere utilizzata come tipo di parametro formale: il problema deriva dal fatto che Java non conosce il tipo di elenco in realtà. Deve essere un tipo ESATTO e UNICO. Spero possa essere d'aiuto :)


2

Mi ha confuso anche se ho letto le risposte qui, fino a quando non ho trovato il commento di Pavel Minaev:

Si noti che Elenco <? estende Numero> non significa "elenco di oggetti di tipi diversi, tutti estendono Numero". Significa "elenco di oggetti di un singolo tipo che estende il numero"

Dopo questo sono stato in grado di capire la fantastica spiegazione di BertF. Elenco <? estende Numero> significa? potrebbe essere di qualsiasi tipo estendendo il numero (intero, doppio, ecc.) e non è chiarito nella dichiarazione (elenco <? estende il numero> elenco) quello che è, quindi quando vuoi usare il metodo add non è noto se l'input è dello stesso tipo o no; qual'è il tipo?

Quindi gli elementi di List <? extends Number> può essere impostato solo durante la costruzione.

Nota anche questo: quando usiamo i template, stiamo dicendo al compilatore con quale tipo stiamo giocando. T per esempio detiene quel tipo per noi, ma no ? fa lo stesso

Devo dire .. Questo è uno di quelli sporchi da spiegare / imparare


1

Invece potresti farlo:

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

0

Puoi spostarlo creando un riferimento all'elenco con un tipo diverso.

(Questi sono i "cast non sicuri" menzionati da 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);

Perché untypedList, superclassedListe trulyclassedListsono solo riferimenti a list, continuerai ad aggiungere elementi alla ArrayList originale.

In realtà non è necessario utilizzarlo (List<?>)nell'esempio sopra, ma potresti averne bisogno nel tuo codice, a seconda del tipo che listti è stato dato.

Si noti che l'utilizzo ?fornirà avvisi del compilatore, fino a quando non si inserisce questo sopra la funzione:

@SuppressWarnings("unchecked")

-1

dove estendi la lista da 'Object', puoi usare list.add e quando vuoi usare list.get devi solo lanciare Object sul tuo Object;

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.