¿Hay alguna diferencia entre
List<Map<String, String>>
y
List<? extends Map<String, String>>
?
Si no hay diferencia, ¿cuál es el beneficio de usar ? extends
?
java
generics
inheritance
polymorphism
Eng.Fouad
fuente
fuente
Respuestas:
La diferencia es que, por ejemplo, un
es un
pero no un
Entonces:
Se podría pensar que una
List
deHashMap
s debería ser unaList
deMap
s, pero hay una buena razón por la cual no lo es:Supongamos que pudieras hacer:
Entonces esta es la razón por la cual una
List
deHashMap
s no debería ser unaList
deMap
s.fuente
HashMap
seMap
debe al polimorfismo.List
deHashMap
s no es unaList
deMap
s.List<Map<String,String>> maps = hashMaps;
yHashMap<String,String> aMap = new HashMap<String, String>();
, todavía encontrará quemaps.add(aMap);
es ilegal mientras quehashMaps.add(aMap);
es legal. El propósito es evitar la adición de tipos incorrectos, pero no permitirá la adición de los tipos correctos (el compilador no puede determinar el tipo "correcto" durante el tiempo de compilación)HashMap
a una lista deMap
s, ambos de sus ejemplos son legales, si yo los estoy leyendo correctamente.No puede asignar expresiones con tipos como
List<NavigableMap<String,String>>
el primero.(Si desea saber por qué no puede asignar
List<String>
paraList<Object>
ver un millón de otras preguntas sobre SO).fuente
List<String>
no es un subtipo deList<Object>
? - ver, por ejemplo, stackoverflow.com/questions/3246137/…? extends
. Tampoco explica la correlación con super / subtipos o co / contravarianza (si hay alguna).Lo que me falta en las otras respuestas es una referencia a cómo esto se relaciona con la covarianza y la contravarianza y los sub y supertipos (es decir, el polimorfismo) en general y Java en particular. Esto puede ser bien entendido por el OP, pero por si acaso, aquí va:
Covarianza
Si tienes una clase
Automobile
, entoncesCar
yTruck
son sus subtipos. Cualquier automóvil puede asignarse a una variable de tipo Automóvil, esto es bien conocido en OO y se llama polimorfismo. La covarianza se refiere al uso de este mismo principio en escenarios con genéricos o delegados. Java no tiene delegados (todavía), por lo que el término se aplica solo a los genéricos.Tiendo a pensar en la covarianza como polimorfismo estándar, lo que esperarías que funcione sin pensar, porque:
Sin embargo, la razón del error es correcta:
List<Car>
no hereda deList<Automobile>
y, por lo tanto, no se pueden asignar entre sí. Solo los parámetros de tipo genérico tienen una relación de herencia. Uno podría pensar que el compilador de Java simplemente no es lo suficientemente inteligente como para comprender adecuadamente su escenario allí. Sin embargo, puedes ayudar al compilador dándole una pista:Contravarianza
El reverso de la covarianza es la contravarianza. Donde en covarianza los tipos de parámetros deben tener una relación de subtipo, en contravarianza deben tener una relación de supertipo. Esto puede considerarse como un límite superior de herencia: cualquier supertipo está permitido e incluye el tipo especificado:
Esto se puede usar con Collections.sort :
Incluso podría llamarlo con un comparador que compara objetos y usarlo con cualquier tipo.
¿Cuándo usar contra o covarianza?
Un poco OT quizás no lo preguntaste, pero ayuda a entender la respuesta a tu pregunta. En general, cuando obtienes algo, usa covarianza y cuando pones algo, usa contravarianza. Esto se explica mejor en una respuesta a la pregunta de desbordamiento de pila ¿Cómo se puede utilizar en contravarianza genéricos de Java? .
Entonces, ¿qué pasa con
List<? extends Map<String, String>>
Usas
extends
, entonces se aplican las reglas para la covarianza . Aquí tiene una lista de mapas y cada elemento que almacene en la lista debe ser unMap<string, string>
derivado de él. La declaraciónList<Map<String, String>>
no puede derivar deMap
, pero debe ser aMap
.Por lo tanto, lo siguiente funcionará, porque
TreeMap
hereda deMap
:pero esto no:
y esto tampoco funcionará, porque no satisface la restricción de covarianza:
¿Qué más?
Esto probablemente sea obvio, pero es posible que ya haya notado que usar la
extends
palabra clave solo se aplica a ese parámetro y no al resto. Es decir, lo siguiente no se compilará:Suponga que desea permitir cualquier tipo en el mapa, con una clave como cadena, puede usar
extend
en cada parámetro de tipo. Es decir, suponga que procesa XML y desea almacenar AttrNode, Element, etc. en un mapa, puede hacer algo como:fuente
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());
resultados enfound: ? 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 perfectamente. El último ejemplo es obviamente correcto.Hoy, he usado esta función, así que aquí está mi ejemplo muy fresco de la vida real. (He cambiado los nombres de clase y método a genéricos para que no distraigan del punto real).
Tengo un método que ha significado para aceptar una
Set
deA
objetos que originalmente escribí con esta firma:Pero en realidad quiere llamarlo con
Set
s de subclases deA
. ¡Pero esto no está permitido! (La razón de esto es quemyMethod
podría agregar objetosset
que sean de tipoA
, pero no del subtipo queset
se declara que los objetos están en el sitio de la persona que llama. Por lo tanto, esto podría romper el sistema de tipos si fuera posible).Ahora aquí vienen los genéricos al rescate, porque funciona según lo previsto si uso la firma de este método en su lugar:
o más corto, si no necesita usar el tipo real en el cuerpo del método:
De esta manera,
set
el tipo se convierte en una colección de objetos del subtipo real deA
, por lo que es posible usar esto con subclases sin poner en peligro el sistema de tipos.fuente
Como mencionó, podría haber dos versiones siguientes de definir una Lista:
List<? extends Map<String, String>>
List<?>
2 es muy abierto. Puede contener cualquier tipo de objeto. Esto puede no ser útil en caso de que desee tener un mapa de un tipo dado. En caso de que alguien accidentalmente ponga un tipo diferente de mapa, por ejemplo
Map<String, int>
,. Su método de consumo podría romperse.Para garantizar que
List
pueda contener objetos de un tipo dado, se introdujeron los genéricos de Java? extends
. Entonces, en el n. ° 1,List
puede contener cualquier objeto derivado delMap<String, String>
tipo. Agregar cualquier otro tipo de datos arrojaría una excepción.fuente