type - En Java, ¿qué puede hacer un comodín que los genéricos normales no pueden hacer?
java wildcard capture (3)
Soy nuevo en Java. En this documento dan esto como un caso de uso para el uso de comodines:
static void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
Esta es su solución:
static void printCollection(Collection<?> c) {
for (Object e : c) {
System.out.println(e);
}
}
Pero podría hacer lo mismo sin un comodín:
static <T> void printCollection(Collection<T> c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}
¿Puede alguien mostrarme un caso de uso simple en el que los genéricos normales no funcionen pero el comodín sí?
Actualización : Las respuestas aquí ¿ Cuándo usar comodines en Java Genéricos? NO nos diga la necesidad del comodín. De hecho es al revés.
T representa el tipo genérico de esa estructura de datos. En su último ejemplo, no lo usa, y NO es un tipo real (por ejemplo, String), y como no lo usa, en realidad no importa en este caso.
Por ejemplo, si tenía una Colección y trató de pasarla a un método que acepta una Colección, eso funciona porque no hay un tipo T en la ruta de clase, por lo que se considera una variable. Si intentaste pasar la misma Colección a un método que acepta una Colección, eso no funcionaría porque tienes una Cadena en tu ruta de clase, por lo que no es una variable.
Toma la lista como ejemplo.
-
List<?>
Puede ser la clase padre deList<A>
.
por ejemplo,
List<B> bList = new ArrayList<>(); // B is a class defined in advance
List<?> list = bList;
Nunca puedes usar <T>
en esta situación.
-
<?>
tiene la captura de comodín.
aquí,
void foo(List<?> i) {
i.set(0, i.get(0));
}
El código de arriba no puede ser compilado. Tú puedes arreglarlo:
void foo(List<?> i) {
fooHelper(i);
}
// wildcard can be captured through type inference.
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
ver más, http://docs.oracle.com/javase/tutorial/java/generics/capture.html
Sólo puedo pensar en los dos actualmente, puede actualizar más tarde.
Una cosa que los comodines nos permiten hacer es declarar tipos que son agnósticos para un parámetro de tipo particular, por ejemplo, una "lista de cualquier tipo de lista" :
List<List<?>> listOfAnyList = ...;
listOfAnyList.add( new ArrayList<String>() );
listOfAnyList.add( new ArrayList<Double>() );
Esto es imposible sin un comodín: * porque las listas de elementos pueden tener diferentes tipos entre sí.
Y si tratamos de capture , encontraremos que no podemos:
static <E> void m(List<List<E>> listOfParticularList) {}
m( listOfAnyList ); // <- this won''t compile
Otra cosa que los comodines nos permiten hacer que los parámetros de tipo no se pueden establecer es un límite inferior. (Un parámetro de tipo se puede declarar con un límite de extends
, pero no un super
límite. **)
class Protector {
private String secretMessage = "abc";
void pass(Consumer<? super String> consumer) {
consumer.accept( secretMessage );
}
}
Supongamos que en su lugar se declara que pass
toma una Consumer<String>
. Ahora supongamos que tenemos un Consumer<Object>
:
class CollectorOfAnything implements Consumer<Object> {
private List<Object> myCollection = new ArrayList<>();
@Override
public void accept(Object anything) {
myCollection.add( anything );
}
}
El problema es: no podemos pasarlo a un método que acepte Consumer<String>
. Declarar Consumer<? super String>
Consumer<? super String>
significa que podemos pasar a cualquier consumidor que acepte un String
. (También vea Java Generics: ¿Qué es PECS?. )
La mayoría de las veces, los comodines nos permiten hacer declaraciones ordenadas.
Si no necesitamos usar un tipo, no tenemos que declarar un parámetro de tipo para él.
* Técnicamente también es posible con un tipo sin formato , pero se desalientan los tipos sin formato.
** No sé por qué Java no permite super
para un parámetro de tipo. 4.5.1. Los argumentos de tipo de tipos parametrizados pueden sugerir que tiene algo que ver con una limitación de inferencia de tipo:
A diferencia de las variables de tipo ordinarias declaradas en una firma de método, no se requiere una inferencia de tipo cuando se usa un comodín. En consecuencia, está permitido declarar límites inferiores en un comodín [...].