2009-08-06 16 views
8
public void wahey(List<Object> list) {} 

wahey(new LinkedList<Number>()); 

La llamada al método no se verificará. Ni siquiera puedo emitir el parámetro como sigue:¿Por qué la Lista <Number> no es un subtipo de la Lista <Object>?

wahey((List<Object>) new LinkedList<Number>()); 

De mi investigación, he deducido que la razón para no permitir este tipo es la seguridad. Si se nos permitió hacer lo anterior, entonces podríamos tener lo siguiente:

List<Double> ld; 
wahey(ld); 

Dentro de la wahey método, podríamos añadir algunas cadenas a la lista de entrada (como el parámetro mantiene una referencia List<Object>). Ahora, después de la llamada al método, ld se refiere a una lista con un tipo List<Double>, ¡pero la lista actual contiene algunos objetos String!

Esto parece diferente a la forma normal en que Java funciona sin genéricos. Por ejemplo:

Object o; 
Double d; 
String s; 

o = s; 
d = (Double) o; 

Lo que estamos haciendo aquí es esencialmente el mismo, excepto que esto pasará controles de tiempo de compilación y sólo un error en tiempo de ejecución. La versión con Listas no se compilará.

Esto me lleva a creer que esto es puramente una decisión de diseño con respecto a las restricciones de tipo en los genéricos. Esperaba obtener algunos comentarios sobre esta decisión?

+1

relacionado (no es un duplicado exacto, aunque la respuesta correcta es básicamente la misma): http: // stackoverflow.com/questions/251298/why-is-someclass-super-t-not-equivalent-to-someclasst-in-java-generic-types – Kip

+3

El término artístico es ** covarianza ** - Menciono esto porque hace es más fácil buscarlo, incluso en Desbordamiento de pila (si desea ver lo que dije al respecto, en el contexto de C#, pero también se aplica a Java) busque [usuario de covarianza: 95810], por ejemplo). –

+0

Un molde doble como 'wahey ((Lista ) (Lista ) nuevo ArrayList ())' se compilará, aunque esto no se recomienda. – Robin479

Respuesta

9

Lo que está haciendo en el ejemplo "sin genéricos" es un molde, lo que deja en claro que está haciendo algo inseguro. El equivalente con los genéricos sería:

Object o; 
List<Double> d; 
String s; 

o = s; 
d.add((Double) o); 

que se comporta de la misma manera (compila, pero falla en tiempo de ejecución). La razón para no permitir el comportamiento que está preguntando es porque permitiría implícitas acciones inseguras, que son mucho más difíciles de detectar en el código. Por ejemplo:

public void Foo(List<Object> list, Object obj) { 
    list.add(obj); 
} 

Esto se ve perfectamente bien y con seguridad de tipos hasta que llame así:

List<Double> list_d; 
String s; 

Foo(list_d, s); 

Lo que también se ve con seguridad de tipos, ya que como la persona que llama no necesariamente saber qué Foo va a hacer con sus parámetros.

De modo que en ese caso tiene dos bits de código aparentemente seguros para el tipo de letra, que juntos terminan siendo inseguros. Eso es malo, porque está oculto y, por lo tanto, es difícil de evitar y más difícil de depurar.

+0

Ah, ya veo. Muchas gracias. :) –

8

Considere si era ...

List<Integer> nums = new ArrayList<Integer>(); 
List<Object> objs = nums 
objs.add("Oh no!"); 
int x = nums.get(0); //throws ClassCastException 

que sería capaz de añadir nada del tipo de los padres a la lista, que puede no ser lo que fue declarado anteriormente como, que en el ejemplo anterior demuestra , causa todo tipo de problemas. Por lo tanto, no está permitido.

+0

Esto es correcto, pero la pregunta indica que ya entiende esto, y quiere saber por qué se tomó la decisión de prohibir este comportamiento al tiempo que permite otras formas de comportamiento inseguro. –

+0

Ya entendí este ejemplo. Traté de explicar este escenario en la pregunta (como dijo Tyler), pero gracias por el comentario. –

+1

este tipo de comportamiento inseguro en realidad * está * permitido con matrices. Lo siguiente compila muy bien, pero genera un error de tiempo de ejecución: 'Object [] arr = new Integer [2]; arr [0] = "¡oh no!"; ' – Kip

4

No son subtipos debido a la forma en que funcionan los genéricos. Lo que se quiere es declarar su función como esta:

public void wahey(List<?> list) {} 

entonces se aceptarán la lista de cualquier cosa que se extiende de objetos.También puede hacer:

public void wahey(List<? extends Number> list) {} 

Esto le permitirá tomar en listas de algo que es una subclase de Número.

Recomendaría que tomara una copia de "Java Generics and Collections" de Maurice Naftalin & Philip Wadler.

+0

+1, así es como funcionan los genéricos. ** DO ** recoge una copia de genéricos y colecciones de Java. Impresionante libro! – WolfmanDragon

+0

Esta no era mi pregunta. Sé que estas son las reglas de subtipos con genéricos. –

3

Existen esencialmente dos dimensiones de la abstracción aquí, la abstracción de la lista y la abstracción de sus contenidos. Está perfectamente bien variar a lo largo de la abstracción de la lista, por ejemplo, que es una lista enlazada o una lista de arreglos, pero no está bien restringir aún más el contenido, decir: esto (lista que contiene objetos) es una lista enlazada que contiene solo números). Debido a que cualquier referencia que lo conozca como (lista que contiene objetos) entiende, mediante el contrato de su tipo, que puede contener cualquier objeto.

Esto es bastante diferente de lo que ha hecho en el código de ejemplo no genérico, donde ha dicho: trate esta cadena como si fuera un doble. En su lugar, intenta decir: trate esto (lista que contiene solo números) como una (lista que contiene cualquier cosa). Y no es así, y el compilador puede detectarlo, por lo que no le permite salirse con la suya.

+0

Buena explicación, gracias. –

1

"What we are doing here is essentially the same thing, except this will pass compile-time checks and only fail at run-time. The version with Lists won't compile."

Lo que estamos observando tiene mucho sentido si tenemos en cuenta que el objetivo principal de genéricos de Java es conseguir incompatibilidades de tipo fallen en tiempo de compilación en lugar de tiempo de ejecución.

De java.sun.com

Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.

0

En Java, List<S>no es un subtipo deList<T> cuando S es un subtipo de T. Esta regla proporciona seguridad de tipo.

Digamos que permitimos que un List<String> sea un subtipo de List<Object>. Consideremos el siguiente ejemplo:

public void foo(List<Object> objects) { 
    objects.add(new Integer(42)); 
} 

List<String> strings = new ArrayList<String>(); 
strings.add("my string"); 
foo(strings); // this is not allow in java 
// now strings has a string and an integer! 
// what would happen if we do the following...?? 
String myString = strings.get(1); 

Así, obligando a que esto proporciona seguridad de tipos, pero también tiene un inconveniente, es menos flexible. Consideremos el siguiente ejemplo:

class MyCollection<T> { 
    public void addAll(Collection<T> otherCollection) { 
     ... 
    } 
} 

Aquí tienes una colección de T 's, que desea añadir todos los artículos de otra colección. No puede llamar a este método con un Collection<S> para un subtipo S de T. Idealmente, esto está bien porque solo está agregando elementos a su colección, no está modificando la colección de parámetros.

Para solucionar esto, Java proporciona lo que llaman "comodines". Los comodines son una forma de proporcionar covarianza/contravarianza. Ahora considere los siguientes utilizando comodines:

class MyCollection<T> { 
    // Now we allow all types S that are a subtype of T 
    public void addAll(Collection<? extends T> otherCollection) { 
     ... 

     otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T) 
    } 
} 

Ahora, con comodines permitimos covarianza en el tipo T y bloqueamos operaciones que no son de tipo caja de seguridad (por ejemplo, la adición de un elemento en la colección). De esta manera obtenemos flexibilidad y tipo de seguridad.

Cuestiones relacionadas