Java Generics (Wildcards)

109

Tenho algumas dúvidas sobre caracteres curinga genéricos em Java:

  1. Qual é a diferença entre List<? extends T>e List<? super T>?

  2. O que é um curinga limitado e o que é um curinga ilimitado?

Pablo Fernandez
fonte
No caso de a resposta de Ted Gao ser excluída (já que era apenas um link), aqui está a postagem do blog ao qual ela tinha um link .
royhowie,

Respostas:

123

Em sua primeira pergunta, <? extends T>e <? super T>são exemplos de curingas delimitados. Um curinga ilimitado se parece com <?>e basicamente significa <? extends Object>. Isso significa vagamente que o genérico pode ser de qualquer tipo. Um caractere curinga limitado ( <? extends T>ou <? super T>) restringe o tipo, dizendo que ele deve estender um tipo específico ( <? extends T>é conhecido como limite superior) ou deve ser um ancestral de um tipo específico ( <? super T>conhecido como limite inferior) .

Os Tutoriais Java têm algumas explicações muito boas sobre genéricos nos artigos Wildcards e More Fun with Wildcards .

Bill the Lizard
fonte
Só para acertar, se A <B e B <C então: <A extends C> está errado?
Pablo Fernandez
Se por A <B, você quer dizer que A estende B, então A estende C. Você não usaria isso na sintaxe curinga, porém, diria <? estende C> para limitar suas escolhas a A ou B.
Bill the Lizard,
e, nesse caso, se eu disser <? super C> qual seria a diferença?
Pablo Fernandez
3
Só queria recomendar outra referência sobre Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Zach Scrivena
3
@Pablo: <? super C>significaria que seu tipo está restrito a algo acima Cna hierarquia de tipos. (Desculpe pela resposta extremamente tardia. Acho que não recebemos notificações de comentários há 2 anos?)
Bill the Lizard
49

Se você tem uma hierarquia de classes A, B é uma subclasse de A, e C e D são subclasses de B como abaixo

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Então

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Um curinga limitado é como ? extends Bonde B é algum tipo. Ou seja, o tipo é desconhecido, mas um "limite" pode ser colocado nele. Nesse caso, ele é limitado por alguma classe, que é uma subclasse de B.

oxbow_lakes
fonte
Presumo que List<? super B>descreve como List aceita o tipo que é a classe pai da classe B ? É por isso que C e D não compilam hmm?
Volkan Güven
38

Josh Bloch também tem uma boa explicação de quando usar supere extendsnesta conversa de vídeo do google io onde ele menciona o mnemônico Produtor extendsConsumidorsuper .

Dos slides da apresentação:

Suponha que você queira adicionar métodos em massa para Stack<E>

void pushAll(Collection<? extends E> src);

- src é um produtor E

void popAll(Collection<? super E> dst);

- dst é um consumidor E

em branco
fonte
Eu li o livro de Bloch, mas ainda não consigo ver a diferença entre extends e super neste caso específico.
Pablo Fernandez
Assista ao vídeo, acho bem claro. Além disso, acho que você deve fazer outra pergunta sobre "qual é a diferença entre List <? Extends T> e List <? Super T>", onde espero obter mais respostas. (se quiser, adicione um link a partir daqui)
branco
2
Camas - por que não incluir o exemplo nesta resposta para torná-la completa e independente. Os links são efêmeros.
James Schek,
3

Pode haver momentos em que você desejará restringir os tipos de tipos que podem ser passados ​​para um parâmetro de tipo. Por exemplo, um método que opera em números pode aceitar apenas instâncias de Number ou suas subclasses. É para isso que servem os parâmetros de tipo limitado.

Collection<? extends MyObject> 

significa que pode aceitar todos os objetos que tenham relacionamento IS-A com MyObject (ou seja, qualquer objeto que seja um tipo de myObject ou podemos dizer qualquer objeto de qualquer subclasse de MyObject) ou um objeto da classe MyObject.

Por exemplo:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Então,

Collection<? extends MyObject> myObject; 

irá aceitar apenas MyObject ou filhos de MyObject (ou seja, qualquer objeto do tipo OurObject ou YourObject ou MyObject, mas não qualquer objeto da superclasse de MyObject).

Sandeep Kumar
fonte
1

Em geral,

Se uma estrutura contém elementos com um tipo de formulário ? extends E, podemos obter elementos da estrutura, mas não podemos colocar elementos na estrutura

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Para colocar elementos na estrutura, precisamos de outro tipo de curinga chamado Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }
Prateek Joshi
fonte
1

Os curingas genéricos são criados para tornar os métodos que operam na coleção mais reutilizáveis.

Por exemplo, se um método possui um parâmetro List<A>, só podemos dar List<A>a este método. É um desperdício para a função deste método em algumas circunstâncias:

  1. Se este método só lê objetos de List<A>, então devemos ter permissão para dar List<A-sub>a este método. (Porque A-sub É um A)
  2. Se este método inserir apenas objetos para List<A>, então devemos ter permissão para dar List<A-super>a este método. (Porque A É um Super Super)
taoxiaopang
fonte