Como posso adicionar à lista <? estende Número> estruturas de dados?

153

Eu tenho uma lista que é declarada assim:

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

Eu tentei adicionar 3 ao foo3. No entanto, recebo uma mensagem de erro como esta:

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
fonte
61
Observe que List<? extends Number>isso não significa "lista de objetos de tipos diferentes, todos os quais estendidos Number". Significa "lista de objetos de um único tipo que se estende Number".
Pavel Minaev 13/09/10
1
É melhor verificar a regra PECS, o Produtor estende, o Consumidor super. stackoverflow.com/questions/2723397/…
noego 1/08/16

Respostas:

303

Desculpe, mas você não pode.

A declaração curinga List<? extends Number> foo3significa que a variável foo3pode conter qualquer valor de uma família de tipos (em vez de qualquer valor de um tipo específico). Isso significa que qualquer uma dessas são atribuições legais:

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

Portanto, considerando isso, que tipo de objeto você poderia adicionar a List foo3ele seria legal após qualquer uma das ArrayListatribuições possíveis acima :

  • Você não pode adicionar um Integerporque foo3pode estar apontando para um List<Double>.
  • Você não pode adicionar um Doubleporque foo3pode estar apontando para um List<Integer>.
  • Você não pode adicionar um Numberporque foo3pode estar apontando para um List<Integer>.

Você não pode adicionar nenhum objeto List<? extends T>porque não pode garantir que tipo de objeto Listele realmente está apontando; portanto, não pode garantir que o objeto seja permitido nele List. A única "garantia" é que você só pode ler e obter uma Tou subclasse de T.

A lógica reversa se aplica a super, por exemplo List<? super T>. Estes são legais:

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

Você não pode ler o tipo específico T (por exemplo Number), List<? super T>porque não pode garantir que tipo de Listcoisa realmente está apontando. A única "garantia" que você tem é que você pode adicionar um valor do tipo T(ou qualquer subclasse de T) sem violar a integridade da lista que está sendo apontada.


O exemplo perfeito disso é a assinatura para Collections.copy():

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

Observe como a srcdeclaração da lista usa extendspara permitir que eu passe qualquer lista de uma família de tipos de lista relacionados e ainda garanto que ela produzirá valores do tipo T ou subclasses de T. Mas você não pode adicionar à srclista.

A destdeclaração da lista é usada superpara permitir a passagem de qualquer lista de uma família de tipos de lista relacionados e ainda garantir que eu possa gravar um valor de um tipo específico T nessa lista. Mas não é possível garantir a leitura dos valores do tipo específico T se eu ler a partir da lista.

Portanto, agora, graças aos curingas genéricos, eu posso fazer qualquer uma dessas chamadas com esse método único:

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

Considere esse código confuso e muito amplo para exercitar seu cérebro. As linhas comentadas são ilegais e a razão pela qual é declarada à extrema direita da linha (é necessário rolar para ver algumas delas):

  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
fonte
11
Por que podemos escrever uma frase como `List <? estende Number> foo3 = new ArrayList <Integer> (); `se não pudermos continuar com um elemento nessa lista?
Amit Kumar Gupta
7
@articlestack: Adicionar não é a única coisa útil a fazer com uma lista. Depois que uma lista é preenchida, a leitura de uma lista pode ser útil. Os curingas genéricos permitem escrever código que funciona genericamente para uma família de listas, por exemplo, funciona para List <Integer> ou List <Double>. Por exemplo, veja a assinatura de Collection.copy (). O src Listargumento usa extendspara ler da lista src, enquanto o dest Listargumento usa superpara gravar na lista de destino. Isso permite um método que pode copiar de List<Integer>ou List<Double>para List<Number>ou List<Object>.
Bert F
Bert F, desculpe por um comentário tardio. Existe um erro de digitação em sua passagem, "para adicionar um valor do tipo T (ou subclasse de T)" deve ler-se (ou superclasse de T)?
Vortex
sim, eu também vi @Vortex Não tenho certeza se está certo ou li errado.
Ungeheuer
1
@Vortex - or subclass of Testá correto. Por exemplo, não posso adicionar um Object(superclasse de Number) a List<? super Number> foo3porque foo3pode ter sido atribuído como: List<? super Number> foo3 = new ArrayList<Number>(que pode conter apenas Numberou subclasses de Number). <? super Number>refere-se aos tipos de List<>s aos quais pode ser atribuído foo3- e não aos tipos de coisas que podem ser adicionados / lidos a partir dele. Os tipos de itens que podem ser adicionados / removidos foo3devem ser itens que podem ser adicionados / removidos de qualquer tipo ao List<>qual possa ser atribuído foo3.
Bert F
17

Você não pode (sem lançamentos inseguros). Você só pode ler a partir deles.

O problema é que você não sabe exatamente do que a lista é. Pode ser uma lista de qualquer subclasse de Number; portanto, quando você tenta inserir um elemento nele, não sabe se o elemento realmente se encaixa na lista.

Por exemplo, a lista pode ser uma lista de Bytes; portanto, seria um erro colocar um Floatnela.

sepp2k
fonte
2

"List '<'? Extends Number> é realmente um curinga de limite superior!

O curinga com limite superior diz que qualquer classe que estenda Number ou Number em si pode ser usada como o tipo de parâmetro formal: O problema decorre do fato de o Java não saber qual é realmente o tipo List . Tem que ser um tipo EXATO e ÚNICO. Espero que ajude :)

andygro
fonte
1

Você poderia fazer isso:

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

Foi confuso para mim, apesar de ler as respostas aqui, até encontrar o comentário de Pavel Minaev:

Observe que a lista <? estende Number> não significa "lista de objetos de tipos diferentes, todos os quais estendem Number". Significa "lista de objetos de um único tipo que estende Number"

Depois disso, pude entender a incrível explicação de BertF. Listar <? estende Número> significa? pode ser de qualquer tipo, estendendo Number (Integer, Double, etc) e não está esclarecido na declaração (List <? extends Number> list) qual deles é; portanto, quando você deseja usar o método add, não se sabe se a entrada é do mesmo tipo ou não; qual é o tipo?

Então os elementos da List <? extends Number> só poderia ser definido ao construir.

Observe também: Quando estamos usando modelos, informamos ao compilador com que tipo estamos mexendo. T, por exemplo, mantém esse tipo para nós, mas não ? faz o mesmo

Eu tenho que dizer .. Este é um dos sujos para explicar / aprender

navid
fonte
-1

onde estender a lista de 'Objeto', você pode usar list.add e, quando quiser, use list.get só precisa converter Object para seu Object;

abbasalim
fonte