Qual é a diferença entre ? e object em genéricos Java?

Eu estou usando o Eclipse para me ajudar a limpar algum código para usar genéricos Java corretamente. Na maioria das vezes, ele está fazendo um excelente trabalho ao inferir tipos, mas há alguns casos em que o tipo inferido deve ser o mais genérico possível: Objeto. Mas o Eclipse parece estar me dando uma opção para escolher entre um tipo de object e um tipo de ‘?’.

Então, qual é a diferença entre:

HashMap hash1; 

e

 HashMap hash2; 

Uma instância de HashMap corresponde ao Map Mas não ao Map . Digamos que você queira escrever um método que aceite mapas de String s para qualquer coisa: se você escrever

 public void foobar(Map ms) { ... } 

você não pode fornecer um HashMap . Se você escreve

 public void foobar(Map ms) { ... } 

funciona!

Uma coisa que às vezes é mal entendida nos genéricos de Java é que List não é um subtipo de List . (Mas o String[] é na verdade um subtipo de Object[] , essa é uma das razões pelas quais os genéricos e os arrays não se misturam bem (os arrays em Java são covariantes, os genéricos não são, eles são invariantes )).

Exemplo: Se você gostaria de escrever um método que aceita List s de InputStream s e subtipos de InputStream , você escreveria

 public void foobar(List< ? extends InputStream> ms) { ... } 

A propósito: O Java Eficaz de Joshua Bloch é um excelente recurso quando você gostaria de entender as coisas não tão simples em Java. (Sua pergunta acima também é abordada muito bem no livro.)

Outra maneira de pensar sobre esse problema é que

 HashMap hash1; 

é equivalente a

 HashMap hash1; 

Junte esse conhecimento com o “Princípio Get and Put” na seção (2.4) de Java Generics and Collections :

O princípio Obter e Colocar: use um curinga extends quando você só obtiver valores fora de uma estrutura, usar super curingas quando colocar apenas valores em uma estrutura e não usar um caractere curinga quando ambos forem colocados e colocados.

e o curinga pode começar a fazer mais sentido, espero.

É fácil entender se você lembra que Collection é apenas uma coleção genérica que contém objects do tipo Object , mas Collection< ?> É um tipo super de todos os tipos de collections.

Você não pode colocar nada com segurança em Map , Porque você não sabe que tipo os valores devem ser.

Você pode colocar qualquer object em um Map , porque o valor é conhecido como um Object .

As respostas acima da covariância cobrem a maioria dos casos, mas perdem uma coisa:

“?” é inclusive de “Objeto” na hierarquia de classs. Você poderia dizer que String é um tipo de object e object é um tipo de. Nem tudo combina com Object, mas tudo combina?

 int test1(List< ?> l) { return l.size(); } int test2(List l) { return l.size(); } List< ?> l1 = Lists.newArrayList(); List l2 = Lists.newArrayList(); test1(l1); // compiles because any list will work test1(l2); // compiles because any list will work test2(l1); // fails because a ? might not be an Object test2(l2); // compiled because Object matches Object 

Declarar hash1 como um HashMap hash1 que a variável hash1 pode conter qualquer HashMap que tenha uma chave de String e qualquer tipo de valor.

 HashMap map; map = new HashMap(); map = new HashMap(); map = new HashMap(); 

Todas as opções acima são válidas, porque o map variables pode armazenar qualquer um desses mapas de hash. Essa variável não se importa com o tipo de valor, do hashmap que ele contém.

Ter um curinga não permite, no entanto, colocar qualquer tipo de object em seu mapa. de fato, com o mapa de hash acima, você não pode colocar nada nele usando a variável map :

 map.put("A", new Integer(0)); map.put("B", new Object()); map.put("C", "Some String"); 

Todas as chamadas de methods acima resultarão em um erro em tempo de compilation, porque o Java não sabe qual é o tipo de valor do map interno do HashMap.

Você ainda pode obter um valor do mapa de hash. Embora você “não saiba o tipo do valor” (porque você não sabe que tipo de mapa hash está dentro de sua variável), você pode dizer que tudo é uma subclass de Object e, portanto, o que quer que você saia do map será do tipo Object:

 HashMap myMap = new HashMap<>();// This variable is used to put things into the map. myMap.put("ABC", 10); HashMap map = myMap; Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map). System.out.println(output); 

O bloco de código acima irá imprimir 10 no console.


Então, para finalizar, use um HashMap com curingas quando você não se importa (ou seja, não importa) quais são os tipos do HashMap , por exemplo:

 public static void printHashMapSize(Map< ?, ?> anyMap) { // This code doesn't care what type of HashMap is inside anyMap. System.out.println(anyMap.size()); } 

Caso contrário, especifique os tipos de que você precisa:

 public void printAThroughZ(Map anyCharacterMap) { for (int i = 'A'; i < = 'Z'; i++) System.out.println(anyCharacterMap.get((char) i)); } 

No método acima, precisaríamos saber que a chave do mapa é um Character , caso contrário, não saberíamos que tipo usar para obter valores dele. Todos os objects têm um toString() , no entanto, o mapa pode ter qualquer tipo de object para seus valores. Ainda podemos imprimir os valores.