Tenho algumas dúvidas sobre caracteres curinga genéricos em Java:
Qual é a diferença entre
List<? extends T>
eList<? super T>
?O que é um curinga limitado e o que é um curinga ilimitado?
Tenho algumas dúvidas sobre caracteres curinga genéricos em Java:
Qual é a diferença entre List<? extends T>
e List<? super T>
?
O que é um curinga limitado e o que é um curinga ilimitado?
Respostas:
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 .
<? super C>
significaria que seu tipo está restrito a algo acima C
na hierarquia de tipos. (Desculpe pela resposta extremamente tardia. Acho que não recebemos notificações de comentários há 2 anos?)
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 B
onde 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.
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?
Josh Bloch também tem uma boa explicação de quando usar super
e extends
nesta conversa de vídeo do google io onde ele menciona o mnemônico Produtor extends
Consumidorsuper
.
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
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).
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));
}
}
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:
List<A>
, então devemos ter permissão para dar List<A-sub>
a este método. (Porque A-sub É um A)List<A>
, então devemos ter permissão para dar List<A-super>
a este método. (Porque A É um Super Super)