2011-10-06 16 views
22

No entiendo cuál es el uso de los genéricos de comodines no vinculados. Los genéricos de comodines enlazados con el límite superior <? extends Animal> tienen mucho sentido, porque al usar polimorfismo puedo trabajar con ese tipo o colección. Pero, ¿qué sentido tiene 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 que no se usaron genéricos.¿Cuál es el uso y el punto de los genéricos de comodines independientes en Java?

Respuesta

23

Un tipo no vinculado puede ser útil cuando su método realmente no se preocupa por el tipo real.

Un ejemplo primitiva sería la siguiente:

public void printStuff(Iterable<?> stuff) { 
    for (Object item : stuff) { 
    System.out.println(item); 
    } 
} 

Desde PrintStream.println() puede manejar todos los tipos de referencia (llamando toString()), no cuidado lo que el contenido de la misma es Iterable.

Y la persona que llama puede pasar en un List<Number> o una Set<String> o una Collection<? extends MySpecificObject<SomeType>>.

También tenga en cuenta que no usar los genéricos (que se llama utilizando un tipo de crudo) en absoluto tiene un efecto bastante diferente: hace que el compilador manejar la totalidad objeto como si los genéricos no existen 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.

Otras distinciones importantes es que no se puede agregar cualquier valor (no null) a un Collection<?>, pero puede agregar todos los objetos con el tipo cruda Collection:

Esto no se compilará, porque el El parámetro type de c es un tipo desconocido (= el comodín ?), por lo que no podemos proporcionar un valor que garantice que se pueda asignar a eso (excepto para null, que se puede asignar a todos los tipos de referencia).

Collection<?> c = new ArrayList<String>(); 
c.add("foo"); // compilation error 

Si deja el parámetro de tipo de salida (es decir, utilizar un tipo de prima), a continuación, puede agregar nada 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 advertir no para usar un tipo sin procesar, específicamente por este motivo: elimina cualquier verificación de tipo relacionada con los genéricos.

+2

Pero mi pregunta era: ¿por qué usar genéricos entonces? Puede ser "Iterable" ... no tiene sentido usar comodines – lisak

+1

@Edgar: porque existe un tipo sin formato (sin genéricos) * solo para la compatibilidad con versiones anteriores: desactiva efectivamente todas las comprobaciones de tipos para ese tipo. Mientras usa un comodín, el tipo revisa, pero contra un tipo desconocido. –

+0

¿verificación de tipo contra un tipo desconocido?No recibo la última frase en su respuesta "no puede agregar ningún valor (no nulo) a una Colección , pero puede agregar todos los objetos al tipo sin procesar Colección" – lisak

1

Si utilizas tipos crudos significa que no sabes sobre los genéricos (porque eres perezoso o el código fue escrito hace siglos), usar <?> significa que sabes sobre los genéricos y enfatizar explícitamente que tu código puede funcionar con cualquier tipo de objetos

0

El uso de comodines ilimitados solo tiene sentido, AFAIK, cuando se envuelve el código anterior que no usa genéricos, básicamente Colecciones.

Si nos fijamos en lo que puede hacer con un genérico, es básicamente nada. Si tiene una colección no puede agregar nada, si intenta leer algo siempre obtendrá un Object y así sucesivamente.

Esto, a su vez, ayuda a garantizar que manejará los datos de una manera segura, mientras que usar el tipo sin procesar habría hecho que el compilador ignore cualquier lío que usted haga.

Which methods and fields are accessible/inaccessible through a reference variable of a wildcard parameterized type? de Angelika Langers Java Generics FAQ podría ser de su interés.

5

Cuando necesite realizar una verificación instanceof.

No se puede parametrizar como esto:

Object value; 
if (value instanceof List<String>) { 
    // ... 
} 

por lo que:

Object value; 
if (value instanceof List<?>) { 
    // ... 
} 
+0

Efectivo Java 23 dice que las verificaciones 'instanceof' son un caso de uso aceptable para tipos crudos, p. 'value instanceof List' está bien. ¿Hay alguna ventaja al usar un comodín ilimitado versus un tipo sin formato? Dicho de otra manera, ¿por qué 'value instanceof List ' es mejor que 'value instanceof List'. – Joe

+0

@JoeS [No hay diferencia en tiempo de ejecución en realidad.] (Http://stackoverflow.com/questions/14959240/why-is-instanceof-operator-allowed-on-un-unbounded-wild-card-type-but-not -on-oth/14959326 # comment57791198_14959326) Yo recomendaría usar el comodín para dejar en claro que es una "lista de todo". –

1

Hay casos de uso perfectamente correctos (raros) para comodines no unidos. El SDK contiene algunos de ellos.

Un ejemplo es un método que hace una acción definida en una lista de ningún tipo y no devuelve nada como rotate en Collections:

static void rotate(List<?> list, int distance) 

Otro ejemplo es cuando se desea una lista de los posibles constructores para una clase, el método es:

Constructor<?>[] getConstructors() 

Aquí en ni siquiera es posible utilizar un genérico, ya que, por definición, la matriz contendrá diferentes constructor de cada uno con su propia clase real. Por el contrario, la API utiliza una firma genérica para obtener un solo constructor: Constructor<T> getConstructor(Class<?>... parameterTypes).

La conclusión es que, incluso si se usa principalmente para la compatibilidad con código anterior, todavía hay lugares en los que los genéricos de comodines independientes son la forma correcta.

0

Permítame reformular la pregunta: "¿Cuál es la diferencia entre List<Object> y List<?>"

La respuesta a eso es que List<?> es más restrictivo. Nos dice que tenemos un grupo de objetos de tipo, pero ese tipo no es necesariamente Object.

Dado que no sabemos de qué tipo es, no podemos agregar nada a la lista; todo lo que agreguemos puede ser del tipo incorrecto. De hecho, no podemos pasar ningún argumento del tipo ? a ningún método, no solo a add().

En el lado positivo, cuando se especifica que un método toma List<?>, puede tomar List<String> o List<Integer> o cualquier otro List<>. List<Object> solo puede tomar List<Object>.

Cuestiones relacionadas