2012-09-10 29 views
16

En mi último question (gracias a todos los que me responden), he aprendido la diferencia entre List<Object> y List<?>.Java: comodines de nuevo

Sin embargo, todavía no puedo ver la utilidad de los comodines.

Tengo dos ArrayList s:

ArrayList<Integer> li = new ArrayList<Integer>(Arrays.asList(1,2,3)); 
ArrayList<String> ls = new ArrayList<String>(Arrays.asList("one","two","three")); 

Ahora, mira a los dos bloques de código a continuación:

static void printList(ArrayList<?> list) 
{ 
    for (Object elem: list) 
     System.out.print(elem + " "); 
    System.out.println(); 
} 

y

static <T> void printList(ArrayList<T> list) 
{ 
    for (T elem: list) 
     System.out.print(elem + " "); 
    System.out.println(); 
} 

Cuando llamo:

printList(li); 
printList(ls); 

Ambos métodos devuelven la salida:

1 2 3 
one two three 

Sin embargo, la segunda solución, en el bucle, en lugar de Object s que utilizan tipos parametrizados (mucho más elegante, creo).

Entonces, la pregunta principal sigue siendo: ¿Por qué necesitamos comodines?

+0

comodines se hicieron justo al final del trabajo de genéricos. Citando a Josh Bloch cuando habla de cierres. "Simplemente no podemos permitirnos otro comodín". Creo que tampoco piensa mucho en ellos. ¿Solo uso? cuando están limitados. por ejemplo, RNJ

Respuesta

2

Usando comodines, su código es seguro. En

List myList; 

puede agregar cualquier objeto que desee. Pero en:

List<?> myList; 

sólo se puede añadir un nula.

Solo debe usar List, sin parámetro de tipo, cuando necesita la clase (List.class) o cuando necesita verificar el tipo (instancia de List).

1

Debido

puede modfify lista en caso de <T>list.add((T) new Object());

pero no se puede modificar la lista en caso de ? que asegura que la lista no se modifican. El compilador genera un error si agrega algo que no sea nulo.

Collection<?> c = new ArrayList<String>(); 
c.add(new Object()); // Compile time error 

Dado que no sabemos cuál es el tipo de elemento de c significa, no podemos añadir objetos a la misma. El método add() toma argumentos de tipo E, el tipo de elemento de la colección. Cuando el parámetro de tipo real es?, Significa algún tipo desconocido. Cualquier parámetro que pasemos para agregar debería ser un subtipo de este tipo desconocido. Como no sabemos qué tipo es, no podemos pasar nada. La única excepción es null, que es miembro de todo tipo.

Por otro lado, dado un List<?>, podemos llamar al get() y hacer uso del resultado. El tipo de resultado es un tipo desconocido, pero siempre sabemos que es un objeto.Por tanto, es seguro para asignar el resultado de get() a una variable de tipo Object o pasarlo como un parámetro donde se espera que el tipo de objeto

Más información sobre WildCards

+1

por lo que su respuesta es "¿usar un parámetro de tipo te permite forzosamente hacer algo ridículo y va en contra del sistema de tipos"? – newacct

7

En el Java Generics Tutorial que dice:

Collection<?> c = new ArrayList<String>(); 
c.add(new Object()); // Compile time error 

Dado que no sabemos cuál es el tipo de elemento de c significa, no podemos añadir objetos a la misma. El método add() toma argumentos de tipo E, el tipo de elemento de la colección. Cuando el parámetro de tipo real es?, Significa algún tipo desconocido. Cualquier parámetro que pasemos para agregar debería ser un subtipo de este tipo desconocido. Como no sabemos qué tipo es, no podemos pasar nada. La única excepción es null, que es miembro de todo tipo.

Por otro lado, dada una lista, podemos llamar a get() y hacer uso del resultado. El tipo de resultado es un tipo desconocido, pero siempre sabemos que es un objeto. Por lo tanto, es seguro asignar el resultado de get() a una variable de tipo Object o pasarlo como un parámetro donde se espera el tipo Object.

Así que tener un tipo de comodín asegura que no podemos add() a la lista, con la excepción de null. Si solo llama al get() en la Lista, sabe que es al menos un Objeto.

+0

Usted los explica, pero no revise ningún caso de uso real. Seguramente hay otras maneras de bloquear una llamada 'add()' que no necesita comodines. – Pureferret

4

Digamos que usted tiene esta jerarquía de clases:

Fruit 
Apple extends Fruit 
Orange extends Fruit 

... y algunas listas de cada tipo de frutas:

List<Apple> apples ; y List<Orange> oranges ;

Ahora quieren un List que puede referirse a cualquiera de estas listas.

Así que declaran List<? extends Fruit> fruits ;

No puede utilizar una variable de tipo aquí.

11

Si la pregunta es "utilidad de los comodines":

comodines son útiles cuando sólo se requiere un conocimiento parcial sobre el parámetro de tipo. El "conocimiento parcial" se implementa mediante los límites superior e inferior (? Super T o? Extiende T); si usas solo el comodín no vinculado (?), no quieres decir nada, y no puedes ver dónde los comodines son realmente útiles.

Los comodines se pueden utilizar en composición con parámetros de tipo, para crear relaciones entre el tipo de parámetros de método, tipo de devolución y tipos de excepción. Así un ejemplo de comodín útil es

class ListManager<T> { 
    public void add(T item, List<? super T> list) { 
     [... some useful operation ...] 
     list.add(item); 
    } 
} 

public class Main { 
    public static void main(String[] args) { 
     List<Object> list = new ArrayList<Object>(); 
     Integer item = 10; 
     ListManager<Integer> manager = new ListManager<Integer>(); 
     manager.add(item, list); 
    } 
} 

El método "ListManager.add()" crea una relación entre el tipo de "lista" y el tipo de "elemento". La operación en "lista" siempre es segura, pero puede usar el método "agregar" con la lista de diferentes tipos de parámetros: hemos utilizado la restricción mínima en el parámetro "lista".

(see also jls7 documentation)

2

Tienes razón. Técnicamente, cada vez que vea un comodín en el primer nivel en el tipo de parámetro, podría crear una nueva variable de tipo para él, y usar esa variable de tipo en el lugar de ese comodín, y funcionaría de la misma manera.

Sin embargo, yo diría que es mucho menos elegante que usar un comodín. Una variable de tipo es útil para expresar restricciones entre expresiones diferentes, como cuando hay dos parámetros con la misma variable de tipo, o entre un parámetro y un tipo de retorno. Una variable de tipo utilizada en un solo lugar en el parámetro es un desperdicio, para eso se diseñaron los comodines, una variable de tipo "anónimo" que no se necesita en ningún otro lado. Cuando solo necesita expresar "algún tipo", sin necesidad de restringirlo con otra cosa, ¿por qué complicar la firma de su método con variables de tipo de bonificación?

También hay otros usos de comodines que no se pueden reemplazar las variables de tipo. Por ejemplo, considere un método public List<?> foo(). Devuelve algún tipo de lista, pero no conoce una lista de qué (y no es seguro agregarle nada). No hay forma de expresar eso usando variables tipo sin comodines.

Además, considere los comodines en los parámetros de tipo anidados: puede tener una variable del tipo List<List<?>>, es decir, una lista heterogénea cuyos elementos pueden ser listas de cosas diferentes. Esto tampoco se puede expresar sin usar comodines.

+0

+1 por dar lo que parece ser la única respuesta (no solo de enlace) que aborda la pregunta de '' contra un parámetro de tipo ilimitado '' utilizado en un solo lugar en la firma ("Una variable de tipo utilizada en solo un lugar en el parámetro es un desperdicio ") en lugar de repetir lo que es un comodín. –

1

Uno de los mejores usos para los comodines que encontré son anidamientos complejos de tipos genéricos.

Un simple ejemplo con una lista de Par < T, E >, el comodín hace que el código sea más corto y más legible ya que todos pueden ver que solo estoy interesado en el valor booleano.

ArrayList<Pair<Boolean, OneOrOtherLongClassName>> pairList = ...; 
    for(Pair<Boolean,?> p:pairList) 
    { 
     if(p.first)count++; 
    } 
+0

debería ser 'Pair.First()'? – Pureferret

+0

@Pureferret no, por convenciones de nomenclatura java que serían 'getFirst()' y tiendo a escribir Pair sin métodos get/set dado que es mutable o los miembros son finales. – josefx

+0

Uh, entonces, ¿cómo funciona esto? – Pureferret