java - parameter - public static t
¿Cuál es el uso y el punto de los genéricos comodines sin unir en Java? (6)
No entiendo de qué se trata el uso de genéricos comodines sin unir. Bound comodines genéricos con límite superior <? extends Animal>
<? extends Animal>
tiene mucho sentido, porque al usar polimorfismo puedo trabajar con ese tipo o colección. ¿Pero cuál es el punto de tener genéricos que pueden ser de cualquier tipo? ¿No derrota el propósito de los genéricos? El compilador no encuentra ningún conflicto y después del borrado de tipo sería como si no se hubieran usado genéricos.
Cuando necesite realizar una instanceof
comprobación.
No se puede parametrizar así:
Object value;
if (value instanceof List<String>) {
// ...
}
Tu también:
Object value;
if (value instanceof List<?>) {
// ...
}
Hay (raramente) casos de uso perfectamente correctos para comodines no vinculados. El SDK contiene algunos de ellos.
Un ejemplo es un método que realiza una acción definida en una lista de cualquier tipo y no devuelve nada como rotate
en Collections
:
static void rotate(List<?> list, int distance)
Otro ejemplo es cuando desea enumerar los posibles constructores para una clase, el método es:
Constructor<?>[] getConstructors()
Aquí, ni siquiera es posible usar un genérico, porque, por definición, la matriz contendrá diferentes constructores, cada uno con su propia clase real. Por el contrario, la API utiliza una firma genérica para obtener un único constructor: Constructor<T> getConstructor(Class<?>... parameterTypes)
.
La conclusión es que incluso si se usa principalmente para la compatibilidad con código más antiguo, todavía hay lugares donde los genéricos comodín sin unir son la forma correcta.
Permítame reformular la pregunta:
"¿Cuál es la diferencia entre List<Object>
y List<?>
?"
La respuesta a eso es que la List<?>
Es más restrictiva. Nos dice que tenemos un montón de objetos de algún tipo, pero ese tipo no es necesariamente un Object
.
Como no sabemos qué tipo es ese tipo, no podemos agregar nada a la lista, ya que todo lo que agregamos puede ser de un tipo incorrecto. De hecho, no podemos pasar ningún argumento de ?
escriba a cualquier método, no solo a add()
.
En el lado positivo, cuando especificamos que un método toma List<?>
, Puede tomar List<String>
o List<Integer>
o cualquier otra List<>
. List<Object>
solo puede tomar List<Object>
.
Si bien el uso de tipos sin procesar significa que no sabe acerca de los genéricos (porque es perezoso o el código fue escrito hace años), usar <?>
Significa que sabe sobre genéricos y enfatiza explícitamente que su código puede funcionar con cualquier tipo de objetos .
Un tipo independiente puede ser útil cuando a su método realmente no le importa el tipo real.
Un ejemplo primitivo sería este:
public void printStuff(Iterable<?> stuff) {
for (Object item : stuff) {
System.out.println(item);
}
}
Como PrintStream.println()
puede manejar todos los tipos de referencia (al llamar a toString()
), no nos importa cuál es el contenido real de ese Iterable
.
¿Y la persona que llama puede pasar en una List<Number>
o un Set<String>
o una Collection<? extends MySpecificObject<SomeType>>
Collection<? extends MySpecificObject<SomeType>>
.
También tenga en cuenta que no usar genéricos (lo que se llama usar un tipo en bruto) tiene un efecto bastante diferente: hace que el compilador maneje todo el objeto como si los genéricos no existieran en absoluto . En otras palabras: no solo se ignora el parámetro de tipo de la clase, sino también todos los parámetros de tipo genérico en los métodos.
Otra distinción importante es que no puede agregar ningún valor (que no sea null
) a una Collection<?>
, Pero puede agregar todos los objetos a la Collection
tipo sin procesar:
Esto no se compilará, porque el parámetro de tipo de c
es un tipo desconocido (¿el comodín ?
), Por lo que no podemos proporcionar un valor que se pueda asignar a ese valor (excepto el null
, que se puede asignar a todas las referencias) tipos).
Collection<?> c = new ArrayList<String>();
c.add("foo"); // compilation error
Si deja el parámetro type fuera (es decir, use un tipo raw), entonces puede agregar cualquier cosa a la colección:
Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());
Tenga en cuenta que el compilador le advertirá que no use un tipo en bruto, específicamente por este motivo: elimina cualquier tipo de verificación relacionada con los genéricos.
Usar comodines ilimitados solo tiene sentido, AFAIK, cuando se ajusta código antiguo que no usa genéricos, básicamente Colecciones.
Si miras lo que puedes hacer con un genérico así, básicamente no es nada . Si tienes una colección no puedes agregar nada, si intentas leer algo, siempre obtendrás un Object
y así sucesivamente.
Esto, a su vez, ayuda a garantizar que manejará los datos de una manera segura, mientras que el uso del tipo sin procesar hubiera provocado que el compilador ignorara cualquier desorden que hiciera.
¿Qué métodos y campos son accesibles / inaccesibles a través de una variable de referencia de un tipo parametrizado comodín? Las preguntas frecuentes de Angelika Langers sobre Java Generics pueden ser de interés.