Existe alguma diferença entre
List<Map<String, String>>
e
List<? extends Map<String, String>>
?
Se não houver diferença, qual é o benefício de usar ? extends
?
java
generics
inheritance
polymorphism
Eng.Fouad
fonte
fonte
Respostas:
A diferença é que, por exemplo, um
é um
mas não um
Assim:
Você pensaria que um
List
deHashMap
s deve ser umList
deMap
s, mas há uma boa razão para que não seja:Suponha que você possa fazer:
Portanto, é por isso que um
List
deHashMap
s não deve ser umList
deMap
s.fonte
HashMap
éMap
devido ao polimorfismo.List
deHashMap
s não é umList
deMap
s.List<Map<String,String>> maps = hashMaps;
eHashMap<String,String> aMap = new HashMap<String, String>();
, você ainda acharámaps.add(aMap);
ilegal enquantohashMaps.add(aMap);
legal. O objetivo é impedir a adição de tipos errados, mas não vai permitir a adição de direito tipos (compilador não pode determinar o tipo "certo" durante o tempo de compilação)HashMap
a uma lista deMap
s, ambos os seus exemplos são legais, se eu os estiver lendo corretamente.Você não pode atribuir expressões com tipos como
List<NavigableMap<String,String>>
o primeiro.(Se você quiser saber por que não pode atribuir
List<String>
paraList<Object>
ver um zilhão de outras perguntas no SO).fonte
List<String>
não é um subtipo deList<Object>
? - veja, por exemplo, stackoverflow.com/questions/3246137/…? extends
. Também não explica a correlação com super / subtipos ou co / contravariância (se houver).O que estou perdendo nas outras respostas é uma referência a como isso se relaciona com co- e contravariância e subtipos e supertipos (isto é, polimorfismo) em geral e Java em particular. Isso pode ser bem entendido pelo OP, mas por precaução, aqui vai:
Covariância
Se você tem uma aula
Automobile
, entãoCar
eTruck
são seus subtipos. Qualquer carro pode ser atribuído a uma variável do tipo automóvel, isso é bem conhecido no OO e é chamado polimorfismo. A covariância refere-se ao uso desse mesmo princípio em cenários com genéricos ou delegados. Java ainda não possui delegados, portanto, o termo se aplica apenas aos genéricos.Costumo pensar em covariância como polimorfismo padrão o que você esperaria que funcionasse sem pensar, porque:
A razão do erro é, no entanto, correta:
List<Car>
não herdaList<Automobile>
e, portanto, não pode ser atribuída uma à outra. Somente os parâmetros de tipo genérico têm um relacionamento herdado. Pode-se pensar que o compilador Java simplesmente não é inteligente o suficiente para entender adequadamente o seu cenário. No entanto, você pode ajudar o compilador dando uma dica:Contravariância
O reverso da covariância é contravariância. Onde na covariância os tipos de parâmetros devem ter um relacionamento de subtipo, em contravariância, eles devem ter um relacionamento de supertipo. Isso pode ser considerado como um limite superior de herança: qualquer supertipo é permitido e incluindo o tipo especificado:
Isso pode ser usado com Collections.sort :
Você pode até chamá-lo com um comparador que compara objetos e o usa com qualquer tipo.
Quando usar contra ou co-variação?
Um pouco AT, talvez, você não tenha perguntado, mas ajuda a entender a resposta à sua pergunta. Em geral, quando você obtém algo, use covariância e, quando coloca algo, use contravariância. Isso é melhor explicado em uma resposta à pergunta Stack Overflow Como a contravariância seria usada nos genéricos Java? .
Então, o que é então com
List<? extends Map<String, String>>
Você usa
extends
, então as regras de covariância se aplicam. Aqui você tem uma lista de mapas e cada item armazenado na lista deve serMap<string, string>
ou derivar dele. A declaraçãoList<Map<String, String>>
não pode derivarMap
, mas deve ser aMap
.Portanto, o seguinte funcionará, porque
TreeMap
herda deMap
:mas isso não irá:
e isso também não funcionará, porque não satisfaz a restrição de covariância:
O quê mais?
Isso provavelmente é óbvio, mas você já deve ter notado que o uso da
extends
palavra - chave se aplica apenas a esse parâmetro e não ao restante. Ou seja, o seguinte não será compilado:Suponha que você queira permitir qualquer tipo no mapa, com uma chave como string, que possa ser usada
extend
em cada parâmetro de tipo. Ou seja, suponha que você processe XML e deseje armazenar AttrNode, Element etc. em um mapa, você pode fazer algo como:fonte
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());
resulta emfound: ? extends java.util.Map<java.lang.String,java.lang.String> required: class or interface without bounds
.List<Map<String, String>> mapList = new ArrayList<Map<String, String>>(); mapList.add(new TreeMap<String, String>());
funciona perfeitamente. O último exemplo está obviamente correto.Hoje, eu usei esse recurso, então aqui está meu novo exemplo da vida real. (Alterei os nomes de classe e método para genéricos, para que eles não se distraiam do ponto real.)
Eu tenho um método que deve aceitar um
Set
dosA
objetos que eu escrevi originalmente com esta assinatura:Mas ele realmente quer chamá-lo com
Set
s de subclasses deA
. Mas isso não é permitido! (A razão para isso é:myMethod
poderia adicionar objetos àquelesset
que são do tipoA
, mas não do subtipo queset
os objetos são declarados no site do chamador. Portanto, isso poderia interromper o sistema de tipos, se fosse possível.)Agora, aqui estão os genéricos para o resgate, porque ele funciona conforme o esperado se eu usar essa assinatura de método:
ou mais curto, se você não precisar usar o tipo real no corpo do método:
Dessa forma,
set
o tipo de se torna uma coleção de objetos do subtipo real deA
, portanto, é possível usá-lo com subclasses sem pôr em risco o sistema de tipos.fonte
Como você mencionou, pode haver duas versões abaixo da definição de uma lista:
List<? extends Map<String, String>>
List<?>
2 é muito aberto. Pode conter qualquer tipo de objeto. Isso pode não ser útil caso você queira ter um mapa de um determinado tipo. Caso alguém acidentalmente coloque um tipo diferente de mapa, por exemplo,
Map<String, int>
,. Seu método de consumidor pode quebrar.Para garantir que
List
possam conter objetos de um determinado tipo, os genéricos Java foram introduzidos? extends
. Assim, em # 1, oList
pode conter qualquer objeto derivado doMap<String, String>
tipo. Adicionar qualquer outro tipo de dados geraria uma exceção.fonte