Lista > vs Lista


¿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?

Author: Peter Mortensen, 2012-03-21

5 answers

La diferencia es que, por ejemplo, un

List<HashMap<String,String>>

Es un

List<? extends Map<String,String>>

Pero no a

List<Map<String,String>>

Así que:

void withWilds( List<? extends Map<String,String>> foo ){}
void noWilds( List<Map<String,String>> foo ){}

void main( String[] args ){
    List<HashMap<String,String>> myMap;

    withWilds( myMap ); // Works
    noWilds( myMap ); // Compiler error
}

Pensarías que un List de HashMap s debería ser un List de Maps, pero hay una buena razón por la que no lo es:

Supongamos que usted podría hacer:

List<HashMap<String,String>> hashMaps = new ArrayList<HashMap<String,String>>();

List<Map<String,String>> maps = hashMaps; // Won't compile,
                                          // but imagine that it could

Map<String,String> aMap = Collections.singletonMap("foo","bar"); // Not a HashMap

maps.add( aMap ); // Perfectly legal (adding a Map to a List of Maps)

// But maps and hashMaps are the same object, so this should be the same as

hashMaps.add( aMap ); // Should be illegal (aMap is not a HashMap)

Así que esta es la razón por la que un List de HashMap s no debería ser un List de Maps.

 173
Author: trutheality,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-26 18:25:46

No puede asignar expresiones con tipos como List<NavigableMap<String,String>> a la primera.

(Si quieres saber por qué no puedes asignar List<String> a List<Object> ver a zillion otras preguntas sobre SO.)

 25
Author: Tom Hawtin - tackline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-21 18:41:00

Lo que me falta en las otras respuestas es una referencia a cómo esto se relaciona con la co - y contravarianza y sub - y supertipos (es decir, polimorfismo) en general y con Java en particular. Esto puede ser bien entendido por el OP, pero por si acaso, aquí va:

Covarianza

Si tienes una clase Automobile, entonces Car y Truck son sus subtipos. Cualquier coche puede ser asignado a una variable de tipo Automóvil, esto es bien conocido en OO y se llama polimorfismo. 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 genéricos.

Tiendo a pensar en la covarianza como polimorfismo estándar lo que esperarías que funcionara sin pensar, porque: {[25]]}

List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.

La razón del error es, sin embargo, correcta: List<Car> no hereda de List<Automobile> y por lo tanto no puede asignarse entre sí. Solo los parámetros de tipo genérico tienen una relación heredada. Una podría pensar que el compilador de Java simplemente no es lo suficientemente inteligente como para entender correctamente su escenario allí. Sin embargo, puede ayudar al compilador dándole una pista:

List<Car> cars;
List<? extends Automobile> automobiles = cars;   // no error

Contravarianza

El reverso de la covarianza es la contravarianza. Mientras que en covarianza los tipos de parámetros deben tener una relación subtipo, en contravarianza deben tener una relación supertipo. Esto se puede considerar como un límite superior de herencia: se permite cualquier supertipo e incluyendo el especificado tipo:

class AutoColorComparer implements Comparator<Automobile>
    public int compare(Automobile a, Automobile b) {
        // Return comparison of colors
    }

Esto se puede usar con Colecciones.sort :

public static <T> void sort(List<T> list, Comparator<? super T> c)

// Which you can call like this, without errors:
List<Car> cars = getListFromSomewhere();
Collections.sort(cars, new AutoColorComparer());

Incluso podría llamarlo con un comparador que compara objetos y lo usa con cualquier tipo.

¿Cuándo usar contra o covarianza?

Un poco OT tal vez, usted no preguntó, pero ayuda a entender la respuesta a su pregunta. En general, cuando get algo, utilizar la covarianza y cuando poner algo, contravarianza. Esto se explica mejor en una respuesta to Stack Overflow question ¿Cómo se usaría la contravarianza en genéricos Java?.

Entonces, ¿qué es, entonces, con List<? extends Map<String, String>>

Se usa extends, por lo que se aplican las reglas para covarianza. Aquí tiene una lista de mapas y cada elemento que almacena en la lista debe ser un Map<string, string> o derivar de él. La declaración List<Map<String, String>> no puede derivar de Map, sino que debe ser a Map.

Por lo tanto, lo siguiente funcionará, porque TreeMap hereda de Map:

List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());

Pero esto no:

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());

Y esto tampoco funcionará, porque no satisface la restricción de covarianza:

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new ArrayList<String>());   // This is NOT allowed, List does not implement Map

¿Qué más?

Esto es probablemente obvio, pero es posible que ya haya notado que usar la palabra clave extends solo se aplica a ese parámetro y no al resto. Es decir, lo siguiente no compilará:

List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>())  // This is NOT allowed

Supongamos que desea permitir cualquier tipo en el mapa, con una clave como cadena, puede usar extend en cada tipo parámetro. Es decir, supongamos que procesa XML y desea almacenar AttrNode, Element, etc. en un mapa, puede hacer algo como:

List<? extends Map<String, ? extends Node>> listOfMapsOfNodes = new...;

// Now you can do:
listOfMapsOfNodes.add(new TreeMap<Sting, Element>());
listOfMapsOfNodes.add(new TreeMap<Sting, CDATASection>());
 16
Author: Abel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:46:39

Hoy, he utilizado esta característica, 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 está destinado a aceptar un Set de A objetos que originalmente escribí con esta firma:

void myMethod(Set<A> set)

Pero quiere llamarlo con Set s de subclases de A. Pero esto no está permitido! (La razón de esto es, myMethod podría agregar objetos a set que son de tipo A, pero no del subtipo que los objetos de set se declaran en el sitio de la persona que llama. Así que 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 esta firma de método en su lugar:

<T extends A> void myMethod(Set<T> set)

O más corto, si no necesita usar el tipo real en el cuerpo del método:

void myMethod(Set<? extends A> set)

De esta manera, el tipo de set se convierte en una colección de objetos del subtipo real de A, por lo que se hace posible use esto con subclases sin poner en peligro el sistema de tipos.

 4
Author: Rörd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-12 13:30:46

Como usted mencionó, podría haber dos versiones siguientes de definir una Lista:

  1. List<? extends Map<String, String>>
  2. 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 determinado. En caso de que alguien accidentalmente pone un tipo diferente de mapa, por ejemplo, Map<String, int>. Su método de consumo podría romperse.

Para asegurar que List puede contener objetos de un tipo dado, Java generics introdujo ? extends. Así que en #1, el List puede sostenga cualquier objeto que se derive del tipo Map<String, String>. Agregar cualquier otro tipo de datos arrojaría una excepción.

 0
Author: Funsuk Wangadu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-12 13:29:47