wildcards parameter method generic example español create java generics wildcard

java - parameter - public static t



Cuándo usar comodines en Java Generics? (6)

esto es de HeadFirst Java : (página 575)

Esta:

public <T extends Animal> void takeThing(ArrayList<T> list)

Hace lo mismo que esto:

public void takeThing(ArrayList<? extends Animal> list)

Así que aquí está mi pregunta: si son exactamente lo mismo, ¿por qué no escribimos

public <? extends Animal> void takeThing(ArrayList<?> list)

o

public void takeThing(ArrayList<T extends Animal> list)

Además, ¿cuándo sería útil usar un? en lugar de una T en una declaración de método (como la anterior) con Generics, o para una declaración de clase? ¿Cuales son los beneficios?


El uso de Java Generics Wildcards se rige por el principio GET-PUT (que también se conoce como el principio IN-OUT). Esto indica que: utilice un comodín "extends" cuando solo obtenga valores de una estructura, use un comodín "super" cuando solo ponga valores en una estructura, y no use comodines cuando haga ambas cosas. Esto no se aplica al tipo de devolución de un método. No use un comodín como tipo de devolución . Vea el siguiente ejemplo:

public static<T> void copyContainerDataValues(Container<? extends T> source, Container<? super T> destinationtion){ destination.put(source.get()); }


La gran diferencia entre

public <T extends Animal> void takeThing(ArrayList<T> list)

y

public void takeThing(ArrayList<? extends Animal> list)

es que en el método anterior puede referirse a "T" dentro del método como la clase concreta que se le dio. En el segundo método, no puedes hacer esto.

Aquí un ejemplo más complejo para ilustrar esto:

// here i can return the concrete type that was passed in public <T extends Animal> Map<T, String> getNamesMap(ArrayList<T> list) { Map<T, String> names = new HashMap<T, String>(); for (T animal : list) { names.put(animal, animal.getName()); // i assume there is a getName method } return names; } // here i have to use general Animal public Map<Animal, String> getNamesMap(ArrayList<? extends Animal> list) { Map<Animal, String> names = new HashMap<Animal, String>(); for (Animal animal : list) { names.put(animal, animal.getName()); // i assume there is a getName method } return names; }

Con el primer método, si pasas una Lista de Gatos obtienes un Mapa con Cat como clave. El segundo método siempre devolvería un Mapa con la clave general de Animal.

Por cierto, esto no es una sintaxis java válida:

public <? extends Animal> void takeThing(ArrayList<?> list)

Usando esta forma de declaración de método genérico, debe usar un identificador java válido y no "?".

Editar:

La forma "Tipo de extensión" solo se aplica a la declaración de tipo de variable o parámetro. Dentro de una declinación de método genérico tiene que ser "Tipo de extensión de identificador" ya que puede hacer referencia al "Identificador" desde su método.


La vinculación del tipo a un parámetro de tipo puede ser más poderosa, dependiendo de lo que se supone que debe hacer el método. No estoy seguro de qué se supone que debe hacer takeThing , pero imagine que en general tenemos un método con una de estas firmas de tipo:

public <T extends Animal> void foo(ArrayList<T> list); //or public void foo(ArrayList<? extends Animal> list);

Aquí hay un ejemplo concreto de algo que solo puedes hacer con la primera firma de tipo:

public <T extends Animal> void foo(ArrayList<T> list) { list.add(list.remove(0)); // (cycle front element to the back) }

En este caso, T debe informar al verificador de tipos que el elemento que se elimina de la lista es un elemento OK para agregar a la lista.

No se puede hacer esto con un comodín porque, como el comodín no se ha vinculado a un parámetro de tipo, su contexto no se rastrea (bueno, se rastrea, a través de "capturas", pero no está disponible para aprovechar). Puede obtener más información sobre esto en otra respuesta que he dado: ¿Cómo funcionan los genéricos de los genéricos?


Los comodines tratan sobre la variación co / contra de los genéricos. Trataré de aclarar lo que esto significa al proporcionar algunos ejemplos.

Básicamente se relaciona con el hecho de que para los tipos S y T, donde S es un subtipo de T, un tipo genérico G<S> no es un subtipo válido de G<T>

List<Number> someNumbers = new ArrayList<Long>(); // compile error

Puedes remediar esto con comodines

List<? extends Number> someNumbers = new ArrayList<Long>(); // this works

Tenga en cuenta que no puede incluir nada en dicha lista

someNumbers.add(2L); //compile error

incluso (y más sorprendente para muchos desarrolladores):

List<? extends Long> someLongs = new ArrayList<Long>(); someLongs.add(2L); // compile error !!!

Creo que SO no es el lugar adecuado para discutir eso en detalle. Trataré de encontrar algunos de los artículos y documentos que explican esto con más detalle.


Si escribes ? extends T ? extends T dices "cualquier cosa que sea una T o más específica". Por ejemplo: una List<Shape> puede tener solo Shape s, mientras que una List<? extends Shape> List<? extends Shape> puede tener Shape s, Circle s, Rectangle s, etc.

Si escribes ? super T ? super T dices "cualquier cosa que sea una T o más general". Esto se usa con menos frecuencia, pero tiene sus casos de uso. Un ejemplo típico sería una devolución de llamada: si desea pasar un Rectangle regreso a una devolución de llamada, puede usar la Callback<? super Rectangle> Callback<? super Rectangle> , ya que una Callback<Shape> también podrá manejar Rectangle s.

Aquí está el artículo relevante de Wikipedia .


Si su método takeThing necesita agregar elementos al parámetro list , la versión comodín no se compilará.

El caso interesante es cuando no está agregando a la lista y ambas versiones parecen compilar y funcionar.

En este caso, escribiría la versión comodín cuando desee permitir diferentes tipos de animales en la lista (más flexibilidad) y la versión del parámetro cuando requiera un tipo fijo de animal en la lista: el tipo T.

Por ejemplo, java.util.Collection declara:

interface Collection<E> { ... public boolean containsAll(Collection<?> c); ... }

Y supongamos que tiene el siguiente código:

Collection<Object> c = Arrays.<Object>asList(1, 2); Collection<Integer> i = Arrays.<Integer>asList(1, 2, 3); i.containsAll(c); //compiles and return true as expected

Si java.util.Collection sería:

interface Collection<E> { ... public boolean containsAll(Collection<E> c); ... }

El código de prueba anterior no se compilaría y se reduciría la flexibilidad de la API de recopilación.

Vale la pena señalar que la última definición de containsAll tiene la ventaja de detectar más errores en tiempo de compilación, por ejemplo:

Collection<String> c = Arrays.asList("1", "2"); Collection<Integer> i = Arrays.asList(1, 2, 3); i.containsAll(c); //does not compile, the integer collection can''t contain strings

Pero falla la prueba válida con una Collection<Object> c = Arrays.<Object>asList(1, 2);