objeto metodos manejo instanciar genericos genericas generica datos con comparar clases arreglos java generics collections

manejo - metodos genericos java



Diferencia entre<? super T> y<? extiende T> en Java (15)

Esta pregunta ya tiene una respuesta aquí:

¿Cuál es la diferencia entre la List<? super T> List<? super T> y List<? extends T> List<? extends T> ?

Solía ​​usar List<? extends T> List<? extends T> , pero no me permite agregarle elementos list.add(e) , mientras que la List<? super T> List<? super T> hace.


extends

La declaración de comodín de la List<? extends Number> foo3 List<? extends Number> foo3 significa que cualquiera de estas son asignaciones legales:

List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context) List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number

  1. Lectura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto se le garantiza leer de la List foo3 ?

    • Puede leer un Number porque cualquiera de las listas que podrían asignarse a foo3 contiene un Number o una subclase de Number .
    • No puedes leer un Integer porque foo3 podría estar apuntando a una List<Double> .
    • No puedes leer un Double porque foo3 podría estar apuntando a una List<Integer> .
  2. Escritura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto podría agregar a la List foo3 que sería legal para todas las posibles asignaciones anteriores de ArrayList :

    • No puede agregar un Integer porque foo3 podría estar apuntando a una List<Double> .
    • No puedes agregar un Double porque foo3 podría estar apuntando a una List<Integer> .
    • No puedes agregar un Number porque foo3 podría estar apuntando a una List<Integer> .

¿No puedes agregar ningún objeto a la List<? extends T> List<? extends T> porque no puede garantizar el tipo de List que realmente apunta, por lo que no puede garantizar que el objeto esté permitido en esa List . La única "garantía" es que solo puedes leer de ella y obtendrás una T o subclase de T

super

Ahora considera la List <? super T> List <? super T> .

La declaración de comodín de la List<? super Integer> foo3 List<? super Integer> foo3 significa que cualquiera de estas son tareas legales:

List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context) List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer

  1. Lectura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto se le garantiza recibir cuando lea la List foo3 ?

    • No se le garantiza un Integer porque foo3 podría estar apuntando a una List<Number> o List<Object> .
    • No se le garantiza un Number porque foo3 podría estar apuntando a una List<Object> .
    • La única garantía es que obtendrá una instancia de un Object o subclase de Object (pero no sabe qué subclase).
  2. Escritura : dadas las posibles asignaciones anteriores, ¿qué tipo de objeto podría agregar a la List foo3 que sería legal para todas las posibles asignaciones anteriores de ArrayList :

    • Puede agregar un Integer porque se permite un Integer en cualquiera de las listas anteriores.
    • Puede agregar una instancia de una subclase de Integer porque se permite una instancia de una subclase de Integer en cualquiera de las listas anteriores.
    • No puedes agregar un Double porque foo3 podría estar apuntando a un ArrayList<Integer> .
    • No puede agregar un Number porque foo3 podría estar apuntando a un ArrayList<Integer> .
    • No puede agregar un Object porque foo3 podría estar apuntando a un ArrayList<Integer> .

PECS

Recuerde PECS : "Producer Extends, Consumer Super" .

  • "Producer Extiende" : si necesita una List para producir valores T (desea leer T s de la lista), ¿debe declararlo con ? extends T ? extends T , por ejemplo, List<? extends Integer> List<? extends Integer> . Pero no se puede agregar a esta lista.

  • "Super consumidor" : si necesita una List para consumir valores T (desea escribir T s en la lista), debe declararlo con ? super T ? super T , por ejemplo, List<? super Integer> List<? super Integer> . Pero no hay garantías de qué tipo de objeto puede leer de esta lista.

  • Si necesita leer y escribir en una lista, debe declararlo exactamente sin comodines, por ejemplo, List<Integer> .

Ejemplo

Tenga en cuenta este ejemplo de las preguntas frecuentes de Java Generics . Observe cómo la lista de origen src (la lista de producción) usa la extends , y la lista de destino dest (la lista de consumidores) usa super :

public class Collections { public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (int i = 0; i < src.size(); i++) dest.set(i, src.get(i)); } }

También vea ¿Cómo puedo agregar a la lista <? Extiende Número> estructuras de datos?


Ejemplo, el orden de herencia se asume como O> S> T> U> V

Utilizando extiende la palabra clave,

Correcto:

List<? extends T> Object = new List<T>(); List<? extends T> Object = new List<U>(); List<? extends T> Object = new List<V>();

Incorrecto:

List<? extends T> Object = new List<S>(); List<? extends T> Object = new List<O>();

super palabra clave:

Correcto:

List<? super T> Object = new List<T>(); List<? super T> Object = new List<S>(); List<? super T> Object = new List<O>();

Incorrecto:

List<? super T> Object = new List<U>(); List<? super T> Object = new List<V>();

Agregar objeto: Objeto de lista = nuevo List ();

Object.add(new T()); //error

Pero ¿por qué error? Veamos las posibilidades de inicialización de objetos de lista.

List<? extends T> Object = new List<T>(); List<? extends T> Object = new List<U>(); List<? extends T> Object = new List<V>();

Si usamos Object.add (new T ()); entonces será correcto solo si

List<? extends T> Object = new List<T>();

Pero hay dos posibilidades extra

Objeto de lista = nueva lista (); Objeto de lista = nueva lista (); Si intentamos agregar (nueva T ()) a las dos posibilidades anteriores, dará un error porque T es la clase superior de U y V. intentamos agregar un objeto T [que es (nuevo T ())] a la lista de tipo U y V. El objeto de clase superior (clase base) no se puede pasar a objeto de clase inferior (subclase).

Debido a las dos posibilidades adicionales, Java le da error incluso si usa la posibilidad correcta, ya que Java no sabe a qué Objeto se refiere. Por lo tanto, no puede agregar objetos a List Object = new List (); Como hay posibilidades que no son válidas.

Agregar objeto: Objeto de lista = nuevo List ();

Object.add(new T()); // compiles fine without error Object.add(new U()); // compiles fine without error Object.add(new V()); // compiles fine without error Object.add(new S()); // error Object.add(new O()); // error

Pero ¿por qué el error se produce en los dos anteriores? podemos usar Object.add (new T ()); sólo en las posibilidades de abajo,

List<? super T> Object = new List<T>(); List<? super T> Object = new List<S>(); List<? super T> Object = new List<O>();

Si intentamos usar Object.add (new T ()) en List Object = new List (); y objeto de lista = nueva lista (); entonces dará error Esto se debe a que no podemos agregar el objeto T [que es nuevo T ()] a la Lista Objeto = nueva Lista (); Porque es un objeto de tipo U. No podemos agregar un objeto T [que es nuevo T ()] a U Object porque T es una clase base y U es una subclase. No podemos agregar la clase base a la subclase y es por eso que se produce un error. Esto es igual para el otro caso.


Imagina tener esta jerarquía

1. se extiende

Por escrito

List<? extends C2> list;

usted está diciendo que la list podrá hacer referencia a un objeto de tipo (por ejemplo) ArrayList cuyo tipo genérico es uno de los 7 subtipos de C2 (incluido C2 ):

  1. C2: new ArrayList<C2>(); , (un objeto que puede almacenar C2 o subtipos) o
  2. D1: new ArrayList<D1>(); , (un objeto que puede almacenar D1 o subtipos) o
  3. D2: new ArrayList<D2>(); , (un objeto que puede almacenar D2 o subtipos) o ...

y así. Siete casos diferentes:

1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4 2) new ArrayList<D1>(): can store D1 E1 E2 3) new ArrayList<D2>(): can store D2 E3 E4 4) new ArrayList<E1>(): can store E1 5) new ArrayList<E2>(): can store E2 6) new ArrayList<E3>(): can store E3 7) new ArrayList<E4>(): can store E4

Tenemos un conjunto de tipos "almacenables" para cada caso posible: 7 (rojo) conjuntos aquí representados gráficamente

Como puede ver, no hay un tipo seguro que sea común para todos los casos:

  • no puede list.add(new C2(){}); porque podría ser list = new ArrayList<D1>();
  • no puede list.add(new D1(){}); porque podría ser list = new ArrayList<D2>();

y así.

2. super

Por escrito

List<? super C2> list;

está diciendo que la list podrá hacer referencia a un objeto de tipo (por ejemplo) ArrayList cuyo tipo genérico es uno de los 7 supertipos de C2 (incluido C2 ):

  • A1: new ArrayList<A1>(); , (un objeto que puede almacenar A1 o subtipos) o
  • A2: new ArrayList<A2>(); , (un objeto que puede almacenar A2 o subtipos) o
  • A3: new ArrayList<A3>(); , (un objeto que puede almacenar A3 o subtipos) o ...

y así. Siete casos diferentes:

1) new ArrayList<A1>(): can store A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4 2) new ArrayList<A2>(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4 3) new ArrayList<A3>(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4 4) new ArrayList<A4>(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4 5) new ArrayList<B2>(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4 6) new ArrayList<B3>(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4 7) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4

Tenemos un conjunto de tipos "almacenables" para cada caso posible: 7 (rojo) conjuntos aquí representados gráficamente

Como puede ver, aquí tenemos siete tipos seguros que son comunes a cada caso: C2 , D1 , D2 , E1 , E2 , E3 , E4 .

  • puedes list.add(new C2(){}); Porque, independientemente del tipo de lista a la que hacemos referencia, C2 está permitido
  • puede list.add(new D1(){}); porque, independientemente del tipo de lista a la que hacemos referencia, se permite D1

y así. Probablemente haya notado que estos tipos corresponden a la jerarquía a partir del tipo C2 .

Notas

Aquí la jerarquía completa si desea hacer algunas pruebas.

interface A1{} interface A2{} interface A3{} interface A4{} interface B1 extends A1{} interface B2 extends A1,A2{} interface B3 extends A3,A4{} interface B4 extends A4{} interface C1 extends B2{} interface C2 extends B2,B3{} interface C3 extends B3{} interface D1 extends C1,C2{} interface D2 extends C2{} interface E1 extends D1{} interface E2 extends D1{} interface E3 extends D2{} interface E4 extends D2{}


Las respuestas más votadas cubren los detalles sobre muchos aspectos. Sin embargo, trataría de responder esto de manera diferente.

Hay 2 cosas que debemos considerar,

1. Asignación a la variable de lista

List<? extends X> listvar;

Aquí, cualquier lista de X o lista de subclases de X se puede asignar a listvar.

List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();

List<? super X> listvar;

Aquí, cualquier lista de X o lista de superclases de X se puede asignar a listvar.

List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();

2. Realice la operación de lectura o escritura en la variable de lista

`List<? extends X> listvar;`

Puede usar esta función para aceptar una lista en argumentos de método y realizar cualquier operación en el tipo X (Nota: solo puede leer objetos de tipo X de la lista).

`List<? super Number> listvar;

Puede usar esta función para aceptar una lista en argumentos de método y realizar cualquier operación en el tipo Objeto ya que solo puede leer objetos del tipo Objeto de la lista. Pero sí, lo adicional aquí es que puede agregar objetos de tipo X a la lista.


Lo más confuso aquí es que, independientemente de las restricciones de tipo que especifiquemos, la asignación funciona de una sola manera:

baseClassInstance = derivedClassInstance;

Puedes pensar que Integer extends Number y que un Integer lo haría como <? extends Number> <? extends Number> , pero el compilador te dirá que <? extends Number> cannot be converted to Integer <? extends Number> cannot be converted to Integer (es decir, en el lenguaje humano, es incorrecto que cualquier cosa que extienda el número se pueda convertir en Integer ):

class Holder<T> { T v; T get() { return v; } void set(T n) { v=n; } } class A { public static void main(String[]args) { Holder<? extends Number> he = new Holder(); Holder<? super Number> hs = new Holder(); Integer i; Number n; Object o; // Producer Super: always gives an error except // when consumer expects just Object i = hs.get(); // <? super Number> cannot be converted to Integer n = hs.get(); // <? super Number> cannot be converted to Number // <? super Number> cannot be converted to ... (but // there is no class between Number and Object) o = hs.get(); // Consumer Super hs.set(i); hs.set(n); hs.set(o); // Object cannot be converted to <? super Number> // Producer Extends i = he.get(); // <? extends Number> cannot be converted to Integer n = he.get(); o = he.get(); // Consumer Extends: always gives an error he.set(i); // Integer cannot be converted to <? extends Number> he.set(n); // Number cannot be converted to <? extends Number> he.set(o); // Object cannot be converted to <? extends Number> } }

hs.set(i); está bien porque Integer se puede convertir en cualquier superclase de Number (y no porque Integer sea ​​una superclase de Number , lo cual no es verdadero).

EDIT agregó un comentario acerca de Consumer Extends y Producer Super; no tienen sentido porque especifican, correspondientemente, nada y solo Object . Se recomienda recordar PECS porque CEPS nunca es útil.


Los comodines genéricos apuntan a dos necesidades primarias:

Lectura de una colección genérica Inserción en una colección genérica Hay tres formas de definir una colección (variable) utilizando comodines genéricos. Estos son:

List<?> listUknown = new ArrayList<A>(); List<? extends A> listUknown = new ArrayList<A>(); List<? super A> listUknown = new ArrayList<A>();

List<?> Significa una lista escrita con un tipo desconocido. Esto podría ser una List<A> , una List<B> , una List<String> etc.

List<? extends A> List<? extends A> significa una Lista de objetos que son instancias de la class A , o subclasses of A (por ejemplo, B y C). List<? super A> List<? super A> significa que la lista se escribe ya sea en la A class o en una superclass of A

Lea más: http://tutorials.jenkov.com/java-generics/wildcards.html


Me encanta la respuesta de @Bert F pero esta es la forma en que mi cerebro la ve.

Tengo una X en mi mano. Si quiero escribir mi X en una lista, esa lista debe ser una Lista de X o una Lista de cosas a las que mi X puede ser transmitida al escribirlas, es decir, cualquier superclase de X ...

List<? super X>

Si obtengo una Lista y quiero leer una X fuera de esa Lista, será mejor una Lista de X o una Lista de cosas que pueden ser enviadas a X cuando las leo, es decir, cualquier cosa que se extienda X

List<? extends X>

Espero que esto ayude.


Me gustaría visualizar la diferencia. Supongamos que tenemos:

class A { } class B extends A { } class C extends B { }

List<? extends T> List<? extends T> - leyendo y asignando:

|-------------------------|-------------------|---------------------------------| | wildcard | get | assign | |-------------------------|-------------------|---------------------------------| | List<? extends C> | A B C | List<C> | |-------------------------|-------------------|---------------------------------| | List<? extends B> | A B | List<B> List<C> | |-------------------------|-------------------|---------------------------------| | List<? extends A> | A | List<A> List<B> List<C> | |-------------------------|-------------------|---------------------------------|

List<? super T> List<? super T> - escribiendo y asignando:

|-------------------------|-------------------|--------------------------------------------| | wildcard | add | assign | |-------------------------|-------------------|--------------------------------------------| | List<? super C> | C | List<Object> List<A> List<B> List<C> | |-------------------------|-------------------|--------------------------------------------| | List<? super B> | B C | List<Object> List<A> List<B> | |-------------------------|-------------------|--------------------------------------------| | List<? super A> | A B C | List<Object> List<A> | |-------------------------|-------------------|--------------------------------------------|

En todos los casos:

  • siempre puede obtener Object de una lista independientemente del comodín.
  • siempre puede agregar un null a una lista mutable independientemente del comodín.

Puede revisar todas las respuestas anteriores para comprender por qué .add() está restringido a ''<?>'' , ''<? extends>'' ''<? extends>'' , y en parte a ''<? super>'' ''<? super>'' .

Pero aquí está la conclusión de todo esto si quieres recordarlo y no quieres ir a explorar la respuesta todas las veces:

List<? extends A> List<? extends A> significa que aceptará cualquier List de A y subclase de A Pero no puedes añadir nada a esta lista. Ni siquiera objetos de tipo A

List<? super A> List<? super A> significa que aceptará cualquier lista de A y superclase de A Puede agregar objetos de tipo A y sus subclases.


Sobre la base de la respuesta de Bert F, me gustaría explicar mi comprensión.

Digamos que tenemos 3 clases como

public class Fruit{} public class Melon extends Fruit{} public class WaterMelon extends Melon{}

Aquí tenemos

List<? extends Fruit> fruitExtendedList = … //Says that I can be a list of any object as long as this object extends Fruit.

Ok, ahora tratemos de obtener algún valor de fruitExtendedList

Fruit fruit = fruitExtendedList.get(position) //This is valid as it can only return Fruit or its subclass.

De nuevo vamos a intentar

Melon melon = fruitExtendedList.get(position) //This is not valid because fruitExtendedList can be a list of Fruit only, it may not be //list of Melon or WaterMelon and in java we cannot assign sub class object to //super class object reference without explicitly casting it.

Igual es el caso para

WaterMelon waterMelon = fruitExtendedList.get(position)

Ahora vamos a tratar de establecer algún objeto en fruitExtendedList

Añadiendo objeto de fruta

fruitExtendedList.add(new Fruit()) //This in not valid because as we know fruitExtendedList can be a list of any //object as long as this object extends Fruit. So what if it was the list of //WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.

Añadiendo objeto melon

fruitExtendedList.add(new Melon()) //This would be valid if fruitExtendedList was the list of Fruit but it may //not be, as it can also be the list of WaterMelon object. So, we see an invalid //condition already.

Finalmente vamos a intentar agregar el objeto WaterMelon.

fruitExtendedList.add(new WaterMelon()) //Ok, we got it now we can finally write to fruitExtendedList as WaterMelon //can be added to the list of Fruit or Melon as any superclass reference can point //to its subclass object.

Pero espere, y si alguien decide hacer un nuevo tipo de Limón, digamos, por razones, Salty Limón como

public class SaltyLemon extends Lemon{}

Ahora fruitExtendedList puede ser una lista de Fruit, Melon, WaterMelon o SaltyLemon.

Por lo tanto, nuestra declaración

fruitExtendedList.add(new WaterMelon())

Tampoco es válido.

Básicamente podemos decir que no podemos escribir nada en una lista extendida de frutas.

Esto resume la List<? extends Fruit> List<? extends Fruit>

Ahora veamos

List<? super Melon> melonSuperList= … //Says that I can be a list of anything as long as its object has super class of Melon.

Ahora intentemos obtener algo de valor de melonSuperList

Fruit fruit = melonSuperList.get(position) //This is not valid as melonSuperList can be a list of Object as in java all //the object extends from Object class. So, Object can be super class of Melon and //melonSuperList can be a list of Object type

Del mismo modo, Melon, WaterMelon o cualquier otro objeto no se pueden leer.

Pero ten en cuenta que podemos leer instancias de tipo Objeto.

Object myObject = melonSuperList.get(position) //This is valid because Object cannot have any super class and above statement //can return only Fruit, Melon, WaterMelon or Object they all can be referenced by //Object type reference.

Ahora, intentemos establecer algún valor desde melonSuperList.

Agregar objeto de tipo de objeto

melonSuperList.add(new Object()) //This is not valid as melonSuperList can be a list of Fruit or Melon. //Note that Melon itself can be considered as super class of Melon.

Agregando objeto tipo fruta

melonSuperList.add(new Fruit()) //This is also not valid as melonSuperList can be list of Melon

Añadiendo objeto tipo melón

melonSuperList.add(new Melon()) //This is valid because melonSuperList can be list of Object, Fruit or Melon and in //this entire list we can add Melon type object.

Añadiendo objeto de tipo WaterMelon

melonSuperList.add(new WaterMelon()) //This is also valid because of same reason as adding Melon

Para resumir, podemos agregar Melon o su subclase en melonSuperList y leer solo Objeto de tipo Objeto.


Usando extensiones solo puedes obtener de la colección. No puedes poner en ello. Además, aunque super permite tanto obtener y poner, el tipo de retorno durante la obtención es ? super t .


super es un límite inferior, y se extiende es un límite superior.

De acuerdo con http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :

La solución es utilizar una forma de comodín acotado que aún no hemos visto: comodines con un límite inferior. La sintaxis? super T denota un tipo desconocido que es un supertipo de T (o T en sí; recuerda que la relación de supertipo es reflexiva). Es el doble de los comodines acotados que hemos estado usando, ¿dónde los usamos? extiende T para denotar un tipo desconocido que es un subtipo de T.


Cuándo usar extendido y super

Los comodines son más útiles en los parámetros del método. Permiten la flexibilidad necesaria en las interfaces de método.

La gente a menudo se confunde cuando usar extensiones y cuándo usar súper límites. La regla de oro es el principio get-put. Si obtiene algo de un contenedor parametrizado, su uso se extiende.

int totalFuel(List<? extends Vehicle> list) { int total = 0; for(Vehicle v : list) { total += v.getFuel(); } return total;}

El método totalFuel obtiene vehículos de la lista, les pregunta sobre la cantidad de combustible que tienen y calcula el total. Si coloca objetos en un contenedor parametrizado, use super.

int totalValue(Valuer<? super Vehicle> valuer) { int total = 0; for(Vehicle v : vehicles) { total += valuer.evaluate(v); } return total;}

El método totalValue pone Vehículos en el Valuer. Es útil saber que el límite extendido es mucho más común que el súper.


Lista <? extiende X> no permite agregar nada, excepto nulo, a la lista.

Lista <? super X> permite agregar cualquier cosa que sea: una X (X o su subclase) o nula.


| v

Lista <? extiende X> ? Puede ser cualquier subclase de X o X sí.

Lista <? super X> ? Puede ser cualquier superclase de X o X misma.

^ |