Qual é a diferença entre e ?

Qual é a diferença entre e ?

Por exemplo, quando você dá uma olhada na class java.util.concurrent.LinkedBlockingQueue há a seguinte assinatura para o construtor:

 public LinkedBlockingQueue(Collection c) 

e por um para o método:

 public int drainTo(Collection c) 

O primeiro diz que é “algum tipo que é um ancestral de E”; o segundo diz que é “algum tipo que é uma subclass de E”. (Em ambos os casos, E está tudo bem.)

Então o construtor usa o ? extends E ? extends E formulário ? extends E , portanto, garante que quando ele buscar valores da coleção, todos serão E ou alguma subclass (ou seja, é compatível). O método drainTo está tentando colocar valores na coleção, portanto, a coleção deve ter um tipo de elemento E ou uma superclass .

Por exemplo, suponha que você tenha uma hierarquia de classs como esta:

 Parent extends Object Child extends Parent 

e um LinkedBlockingQueue . Você pode construir essa passagem em uma List que copia todos os elementos com segurança, porque cada Child é pai. Você não pode passar em uma List porque alguns elementos podem não ser compatíveis com o Parent .

Da mesma forma você pode drenar essa fila em uma List porque cada Parent é um Object … mas você não pode drená-lo em uma List porque a List espera que todos os seus elementos sejam compatíveis com Child .

As razões para isso são baseadas em como o Java implementa genéricos.

Um exemplo de matrizes

Com matrizes, você pode fazer isso (matrizes são covariantes)

 Integer[] myInts = {1,2,3,4}; Number[] myNumber = myInts; 

Mas o que aconteceria se você tentasse fazer isso?

 myNumber[0] = 3.14; //attempt of heap pollution 

Esta última linha seria compilada muito bem, mas se você executar este código, você poderá obter uma ArrayStoreException . Porque você está tentando colocar um duplo em um array inteiro (independentemente de ser acessado através de uma referência numérica).

Isso significa que você pode enganar o compilador, mas não pode enganar o sistema de tipos de tempo de execução. E isso é assim porque os arrays são o que chamamos de tipos reificáveis . Isso significa que, no tempo de execução, o Java sabe que esse array foi realmente instanciado como uma matriz de inteiros que simplesmente é acessada por meio de uma referência do tipo Number[] .

Então, como você pode ver, uma coisa é o tipo real do object, e outra coisa é o tipo de referência que você usa para acessá-lo, certo?

O problema com genéricos Java

Agora, o problema com os tipos genéricos Java é que as informações de tipo são descartadas pelo compilador e não estão disponíveis no tempo de execução. Esse processo é chamado de eliminação de tipo . Existem boas razões para implementar genéricos como este em Java, mas isso é uma longa história, e isso tem a ver com compatibilidade binária com código pré-existente.

Mas o ponto importante aqui é que, como em tempo de execução não há informações de tipo, não há como garantir que não estamos comprometendo a poluição de heap.

Por exemplo,

 List myInts = new ArrayList(); myInts.add(1); myInts.add(2); List myNums = myInts; //compiler error myNums.add(3.14); //heap pollution 

Se o compilador Java não impedir você de fazer isso, o sistema de tipos de tempo de execução também não poderá pará-lo, pois não há como, em tempo de execução, determinar que essa lista deveria ser apenas uma lista de inteiros. O Java runtime permitiria que você colocasse o que quiser nessa lista, quando deveria conter apenas inteiros, porque quando foi criado, foi declarado como uma lista de inteiros.

Como tal, os designers de Java garantiram que você não pode enganar o compilador. Se você não pode enganar o compilador (como podemos fazer com arrays) você não pode enganar o sistema de tipos de tempo de execução.

Como tal, dizemos que tipos genéricos não são reificáveis .

Evidentemente, isso prejudicaria o polymorphism. Considere o seguinte exemplo:

 static long sum(Number[] numbers) { long summation = 0; for(Number number : numbers) { summation += number.longValue(); } return summation; } 

Agora você poderia usá-lo assim:

 Integer[] myInts = {1,2,3,4,5}; Long[] myLongs = {1L, 2L, 3L, 4L, 5L}; Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0}; System.out.println(sum(myInts)); System.out.println(sum(myLongs)); System.out.println(sum(myDoubles)); 

Mas se você tentar implementar o mesmo código com collections genéricas, não terá êxito:

 static long sum(List numbers) { long summation = 0; for(Number number : numbers) { summation += number.longValue(); } return summation; } 

Você obteria erros de compilador se tentar …

 List myInts = asList(1,2,3,4,5); List myLongs = asList(1L, 2L, 3L, 4L, 5L); List myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0); System.out.println(sum(myInts)); //compiler error System.out.println(sum(myLongs)); //compiler error System.out.println(sum(myDoubles)); //compiler error 

A solução é aprender a usar dois poderosos resources dos genéricos Java conhecidos como covariância e contravariância.

Covariância

Com a covariância, você pode ler itens de uma estrutura, mas não pode escrever nada nela. Todas estas são declarações válidas.

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

E você pode ler em myNums :

 Number n = myNums.get(0); 

Porque você pode ter certeza de que, qualquer que seja a lista, ela pode ser colocada em um número (afinal, qualquer coisa que estenda o número é um número, certo?)

No entanto, você não tem permissão para colocar nada em uma estrutura covariante.

 myNumst.add(45L); //compiler error 

Isso não seria permitido, porque o Java não pode garantir qual é o tipo real do object na estrutura genérica. Pode ser qualquer coisa que estenda Number, mas o compilador não pode ter certeza. Então você pode ler, mas não escrever.

Contravariância

Com contravariância você pode fazer o oposto. Você pode colocar as coisas em uma estrutura genérica, mas não pode ler a partir dela.

 List myObjs = new List(); myObjs.add("Luke"); myObjs.add("Obi-wan"); List< ? super Number> myNums = myObjs; myNums.add(10); myNums.add(3.14); 

Nesse caso, a natureza real do object é uma Lista de Objetos e, por meio de contravariância, você pode colocar o Numbers nele, basicamente porque todos os números têm Object como seu ancestral comum. Como tal, todos os números são objects e, portanto, isso é válido.

No entanto, você não pode ler com segurança nada dessa estrutura contrária, presumindo que obterá um número.

 Number myNum = myNums.get(0); //compiler-error 

Como você pode ver, se o compilador permitisse que você escrevesse esta linha, você obteria um ClassCastException em tempo de execução.

Princípio Get / Put

Dessa forma, use a covariância quando você pretende obter apenas valores genéricos de uma estrutura, use contravariance quando desejar apenas colocar valores genéricos em uma estrutura e usar o tipo genérico exato quando desejar fazer ambos.

O melhor exemplo que tenho é o seguinte, que copia qualquer tipo de número de uma lista para outra. Só obtém itens da fonte e apenas coloca itens no destino.

 public static void copy(List< ? extends Number> source, List< ? super Number> target) { for(Number number : source) { target(number); } } 

Graças aos poderes de covariância e contravariância, isso funciona para um caso como este:

 List myInts = asList(1,2,3,4); List myDoubles = asList(3.14, 6.28); List myObjs = new ArrayList(); copy(myInts, myObjs); copy(myDoubles, myObjs); 

< ? extends E> < ? extends E> define E como o limite superior: “Isso pode ser convertido em E “.

< ? super E> < ? super E> define E como o limite inferior: ” E pode ser lançado para isso.”

Eu vou tentar responder isso. Mas, para obter uma resposta realmente boa, você deve conferir o livro Effective Java (2ª Edição) de Joshua Bloch. Ele descreve o PECS mnemônico, que significa “Producer Extends, Consumer Super”.

A ideia é que, se o código consumir os valores genéricos do object, você deverá usar as extensões. mas se você está produzindo novos valores para o tipo genérico, você deve usar super.

Então, por exemplo:

 public void pushAll(Iterable< ? extends E> src) { for (E e: src) push(e); } 

E

 public void popAll(Collection< ? super E> dst) { while (!isEmpty()) dst.add(pop()) } 

Mas realmente você deve verificar este livro: http://java.sun.com/docs/books/effective/

< ? super E> < ? super E> significa any object including E that is parent of E

< ? extends E> < ? extends E> significa any object including E that is child of E .

Você pode querer google para os termos contravariance ( < ? super E> ) e covariância ( < ? extends E> ). Descobri que a coisa mais útil ao compreender os genéricos era entender a assinatura do método Collection.addAll :

 public interface Collection { public boolean addAll(Collection< ? extends T> c); } 

Assim como você deseja adicionar uma String a uma List :

 List lo = ... lo.add("Hello") 

Você também deve poder adicionar uma List (ou qualquer coleção de String s) através do método addAll :

 List ls = ... lo.addAll(ls) 

No entanto, você deve perceber que uma List e uma List não são equivalentes e nem a última é uma subclass da primeira. O que é necessário é o conceito de um parâmetro do tipo covariante – isto é, o parâmetro < ? extends T> < ? extends T> bit.

Depois de ter isso, é simples pensar em cenários em que você deseja contravariância também (verifique a interface Comparable ).

Antes da resposta; Por favor, seja claro que

  1. Recurso de tempo de compilation genérico somente para garantir TYPE_SAFETY, ele não estará disponível durante o RUNTIME.
  2. Apenas uma referência com genéricos forçará o tipo de segurança; se a referência não for declarada com genéricos, funcionará sem segurança.

Exemplo:

 List stringList = new ArrayList(); stringList.add(new Integer(10)); // will be successful. 

Espero que isso ajude você a entender o curinga mais claro.

 //NOTE CE - Compilation Error // 4 - For class A {} class B extends A {} public class Test { public static void main(String args[]) { A aObj = new A(); B bObj = new B(); //We can add object of same type (A) or its subType is legal List list_A = new ArrayList(); list_A.add(aObj); list_A.add(bObj); // A aObj = new B(); //Valid //list_A.add(new String()); Compilation error (CE); //can't add other type A aObj != new String(); //We can add object of same type (B) or its subType is legal List list_B = new ArrayList(); //list_B.add(aObj); CE; can't add super type obj to subclass reference //Above is wrong similar like B bObj = new A(); which is wrong list_B.add(bObj); //Wild card (?) must only come for the reference (left side) //Both the below are wrong; //List< ? super A> wildCard_Wrongly_Used = new ArrayList< ? super A>(); //List< ? extends A> wildCard_Wrongly_Used = new ArrayList< ? extends A>(); //Both < ? extends A>; and < ? super A> reference will accept = new ArrayList List< ? super A> list_4__A_AND_SuperClass_A = new ArrayList(); list_4__A_AND_SuperClass_A = new ArrayList(); //list_4_A_AND_SuperClass_A = new ArrayList(); CE B is SubClass of A //list_4_A_AND_SuperClass_A = new ArrayList(); CE String is not super of A List< ? extends A> list_4__A_AND_SubClass_A = new ArrayList(); list_4__A_AND_SubClass_A = new ArrayList(); //list_4__A_AND_SubClass_A = new ArrayList(); CE Object is SuperClass of A //CE; super reference, only accepts list of A or its super classs. //List< ? super A> list_4__A_AND_SuperClass_A = new ArrayList(); //CE; extends reference, only accepts list of A or its sub classs. //List< ? extends A> list_4__A_AND_SubClass_A = new ArrayList(); //With super keyword we can use the same reference to add objects //Any sub class object can be assigned to super class reference (A) list_4__A_AND_SuperClass_A.add(aObj); list_4__A_AND_SuperClass_A.add(bObj); // A aObj = new B(); //list_4__A_AND_SuperClass_A.add(new Object()); // A aObj != new Object(); //list_4__A_AND_SuperClass_A.add(new String()); CE can't add other type //We can't put anything into "? extends" structure. //list_4__A_AND_SubClass_A.add(aObj); compilation error //list_4__A_AND_SubClass_A.add(bObj); compilation error //list_4__A_AND_SubClass_A.add(""); compilation error //The Reason is below //List apples = new ArrayList(); //List< ? extends Fruit> fruits = apples; //fruits.add(new Strawberry()); THIS IS WORNG :) //Use the ? extends wildcard if you need to retrieve object from a data structure. //Use the ? super wildcard if you need to put objects in a data structure. //If you need to do both things, don't use any wildcard. //Another Solution //We need a strong reference(without wild card) to add objects list_A = (ArrayList) list_4__A_AND_SubClass_A; list_A.add(aObj); list_A.add(bObj); list_B = (List) list_4__A_AND_SubClass_A; //list_B.add(aObj); compilation error list_B.add(bObj); private Map, List< ? extends Animal>> animalListMap; public void registerAnimal(Class< ? extends Animal> animalClass, Animal animalObject) { if (animalListMap.containsKey(animalClass)) { //Append to the existing List /* The ? extends Animal is a wildcard bounded by the Animal class. So animalListMap.get(animalObject); could return a List, List, List, assuming Donkey, Mouse, and Pikachu were all sub classs of Animal. However, with the wildcard, you are telling the compiler that you don't care what the actual type is as long as it is a sub type of Animal. */ //List< ? extends Animal> animalList = animalListMap.get(animalObject); //animalList.add(animalObject); //Compilation Error because of List< ? extends Animal> List animalList = animalListMap.get(animalObject); animalList.add(animalObject); } } } } 

Um curinga com um limite superior se parece com “? Extends Type” e representa a família de todos os tipos que são subtipos de Type, type Type sendo incluído. Tipo é chamado o limite superior.

Um curinga com um limite inferior se parece com “? Super Type” e representa a família de todos os tipos que são supertipos de Type, type Type sendo incluído. Tipo é chamado o limite inferior.

Você tem uma class pai e uma class filho herdada da class pai. A class pai é herdada de outra class chamada GrandParent Class.So Ordem de inheritance é GrandParent> Parent> Child. Agora, estende Pai> – Isso aceita class pai ou class infantil < ? super pai> – Isso aceita class pai ou class GrandParent