tipos propiedades example create botones java generics bounded-wildcard unbounded-wildcard

java - propiedades - jcheckbox



Tipo genérico anidado de Java (2)

¿Cómo se debe usar el tipo genérico Map<?, ? extends List<?>> Map<?, ? extends List<?>> lugar de un Map<?, List<?>> más simple Map<?, List<?>> para el siguiente método test() ?

public static void main(String[] args) { Map<Integer, List<String>> mappy = new HashMap<Integer, List<String>>(); test(mappy); } public static void test(Map<?, ? extends List<?>> m) {} // Doesn''t compile // public static void test(Map<?, List<?>> m) {}

Teniendo en cuenta que lo siguiente funciona, y que los tres métodos tienen el mismo tipo borrado de todos modos.

public static <E> void test(Map<?, List<E>> m) {}


Esto se debe a que las reglas de subclasificación para genéricos son ligeramente diferentes de lo que puede esperar. En particular si tiene:

class A{} class B extends A{}

entonces

List<B> no es una subclase de List<A>

here explica en detalle y el uso del comodín (el carácter "?") Se explica here .


Fundamentalmente, List<List<?>> y List<? extends List<?>> List<? extends List<?>> tiene distintos argumentos de tipo.

En realidad, es el caso de que uno es un subtipo del otro, pero primero aprendamos más sobre lo que significan individualmente.

Comprender las diferencias semánticas.

En general, ¿el comodín ? representa alguna "información faltante". Significa que "hubo una discusión de tipo aquí una vez, pero ya no sabemos qué es" . Y debido a que no sabemos qué es, se imponen restricciones sobre cómo podemos usar cualquier cosa que se refiera a ese tipo de argumento en particular.

Por el momento, simplifiquemos el ejemplo usando List lugar de Map .

  • Una List<List<?>> contiene cualquier tipo de Lista con cualquier argumento de tipo . Así que es decir:

    List<List<?>> theAnyList = new ArrayList<List<?>>(); // we can do this theAnyList.add( new ArrayList<String>() ); theAnyList.add( new LinkedList<Integer>() ); List<?> typeInfoLost = theAnyList.get(0); // but we are prevented from doing this typeInfoLost.add( new Integer(1) );

    Podemos poner cualquier List en la List de theAnyList , pero al hacerlo hemos perdido el conocimiento de sus elementos .

  • Cuando usamos ? extends , la List tiene algún subtipo específico de Lista, pero ya no sabemos qué es . Así que es decir:

    List<? extends List<Float>> theNotSureList = new ArrayList<ArrayList<Float>>(); // we can still use its elements // because we know they store Float List<Float> aFloatList = theNotSureList.get(0); aFloatList.add( new Float(1.0f) ); // but we are prevented from doing this theNotSureList.add( new LinkedList<Float>() );

    Ya no es seguro agregar nada a theNotSureList , porque no sabemos el tipo real de sus elementos. ( ¿Era originalmente una List<LinkedList<Float>> ? ¿O una List<Vector<Float>> ? No lo sabemos.)

  • Podemos juntarlos y tener una List<? extends List<?>> List<? extends List<?>> . Ya no sabemos qué tipo de List tiene, y tampoco sabemos el tipo de elemento de esas List . Así que es decir:

    List<? extends List<?>> theReallyNotSureList; // these are fine theReallyNotSureList = theAnyList; theReallyNotSureList = theNotSureList; // but we are prevented from doing this theReallyNotSureList.add( new Vector<Float>() ); // as well as this theReallyNotSureList.get(0).add( "a String" );

    Hemos perdido información sobre theReallyNotSureList , así como el tipo de elemento de la List dentro de ella.

    (Pero puede observar que podemos asignarle cualquier tipo de listas que contengan listas ...)

Así que para descomponerlo:

// ┌ applies to the "outer" List // ▼ List<? extends List<?>> // ▲ // └ applies to the "inner" List

El Map funciona de la misma manera, solo tiene más parámetros de tipo:

// ┌ Map K argument // │ ┌ Map V argument // ▼ ▼ Map<?, ? extends List<?>> // ▲ // └ List E argument

Por que ? extends es necesario

Es posible que sepa que los tipos genéricos "concrete" tienen invariabilidad , es decir, List<Dog> no es un subtipo de List<Animal> incluso si la class Dog extends Animal . En cambio, el comodín es cómo tenemos la covarianza , es decir, List<Dog> es un subtipo de List<? extends Animal> List<? extends Animal> .

// Dog is a subtype of Animal class Animal {} class Dog extends Animal {} // List<Dog> is a subtype of List<? extends Animal> List<? extends Animal> a = new ArrayList<Dog>(); // all parameterized Lists are subtypes of List<?> List<?> b = a;

Entonces aplicando estas ideas a una List anidada:

  • List<String> es un subtipo de List<?> Pero List<List<String>> no es un subtipo de List<List<?>> . Como se mostró anteriormente, esto nos impide comprometer la seguridad de los tipos al agregar elementos incorrectos a la List .
  • List<List<String>> es un subtipo de List<? extends List<?>> List<? extends List<?>> , porque el carácter comodín delimitado permite la covarianza. Es decir, ? extends permite que el hecho de que List<String> es un subtipo de List<?> para ser considerado.
  • List<? extends List<?>> List<? extends List<?>> es, de hecho, un supertipo compartido:

    List<? extends List<?>> ╱ ╲ List<List<?>> List<List<String>>

En revisión

  1. Map<Integer, List<String>> solo acepta la List<String> como un valor.
  2. Map<?, List<?>> acepta cualquier List como un valor.
  3. Map<Integer, List<String>> y Map<?, List<?>> son tipos distintos que tienen una semántica separada.
  4. Uno no se puede convertir al otro, para evitar que hagamos modificaciones de manera insegura.
  5. Map<?, ? extends List<?>> Map<?, ? extends List<?>> es un supertipo compartido que impone restricciones seguras:

    Map<?, ? extends List<?>> ╱ ╲ Map<?, List<?>> Map<Integer, List<String>>

Cómo funciona el método genérico

Al usar un parámetro de tipo en el método, podemos afirmar que la List tiene algún tipo concreto.

static <E> void test(Map<?, List<E>> m) {}

Esta declaración particular requiere que todas las List en el Map tengan el mismo tipo de elemento. No sabemos qué es ese tipo en realidad, pero podemos usarlo de manera abstracta. Esto nos permite realizar operaciones "a ciegas".

Por ejemplo, este tipo de declaración podría ser útil para algún tipo de acumulación:

static <E> List<E> test(Map<?, List<E>> m) { List<E> result = new ArrayList<E>(); for(List<E> value : m.values()) { result.addAll(value); } return result; }

No podemos llamar a m porque no sabemos cuál es su tipo de clave . Sin embargo, podemos manipular sus valores porque entendemos que todos son List con el mismo tipo de elemento .

Sólo por diversión

Otra opción que la pregunta no discute es tener un carácter comodín acotado y un tipo genérico para la List :

static <E> void test(Map<?, ? extends List<E>> m) {}

Podríamos llamarlo con algo como un Map<Integer, ArrayList<String>> . Esta es la declaración más permisiva, si solo nos preocupamos por el tipo de E

También podemos usar límites para anidar parámetros de tipo:

static <K, E, L extends List<E>> void(Map<K, L> m) { for(K key : m.keySet()) { L list = m.get(key); for(E element : list) { // ... } } }

Esto es tanto permisivo acerca de lo que podemos pasarle, como permisivo acerca de cómo podemos manipular m todo lo que contiene.

Ver también