2011-06-28 22 views
6

He la clase siguiente:otra java pregunta genérica

interface Able{/* ... */} 
class A implements Able{/* ... */} 

y tengo

Map<String,? extends Able> as; 
as = new HashMap<String, A>(); 

por qué hace lo siguiente causar un error:

as.put("a", new A()); 

¿Alguna idea?

+2

lo que es exactamente el error? – Bohemian

Respuesta

15

La referencia a los genéricos de java es buena (jdk site).

De hecho @Oli_Charlesworth dio una buena respuesta, pero tal vez esta sea más completa.

En un Collection<? extends Able> no puede insertar nada que sea correcto.

Si tiene

class A implements Able {...} 

y

class B implement Able {...} 

Entonces, Collection<? extends Able> es un tipo súper de ambos:

Collection<A> 
Collection<B> 

Por lo tanto, es legal para escribir alguna declaración como

//Code snippet 01 
Collection< ? extends Able > list; 
Collection<A> listA; 
Collection<B> listB; 
list = listA; 
list = listB; 

Esa es de hecho la razón por la que existe la notación comodín Collection<? extends Able>.

Pero, aquí las cosas son cada vez más interesante:

En un Collection<A> puedes colocar sólo los objetos que se A (incluyendo las subclases). Lo mismo para Collection<B>. En ambos no puedes agregar algo que es solo Able. Por ejemplo:

//Code snippet 02 
listA.add(new A()); //valid at compile-time 
listA.add(new B()); //not valid at compile-time 
listB.add(new B()); //valid at compile-time 
listB.add(new A()); //not valid at compile-time 

Por lo tanto, si el grupo lo que vimos en code snippets 01 & 02, comprenderán que es absolutamente imposible que el compilador para aceptar una declaración como:

Collection< ? extends Able > list; 
list.add(new A());   //not allowed, will work only if list is List<A> 
list.add(new B());   //not allowed, will work only if list is List<B> 

Así que sí, el tipo súper Collection< ? extends Able > no acepta agregar nada. Los tipos más generales ofrecen la intersección de funcionalidades de subtipos y, como tal, menos características que subtipo. Aquí, perdemos la capacidad de agregar objetos A y B. Aquellos función ocurrirá más tarde en la jerarquía ... y aún significa que no podemos añadir nada en la superclase Collection< ? extends Able >

observación adicional:

Además, tenga en cuenta que en un Collection<Able> se puede añadir lo que quieren de esta manera:

Collection<Able> list; 
list.add(new A());   //valid 
list.add(new B());   //valid 

Pero, Collection<Able> no es una superclase de Collection<A> y Collection<B>. Significaría, como con cualquier relación de herencia, que las subclases pueden hacer lo que sea que su superclase puede hacer, ya que la herencia es especialización. Entonces, esto significaría que podríamos agregar objetos A y B a ambas subclases Collection<A> y Collection<B> y ese no es el caso. Así que ya que no es una superclase no se puede tener:

Collection<Able> list; 
Collection<A> listA; 
Collection<B> listB; 
list = listA; //not valid because there is no inheritance hierarchy 
list = listB; //not valid because there is no inheritance hierarchy 

Nota que la herencia es una relación hyperonimic (generalización/especialización) y las colecciones de definir una relación meronimic (contenedor/containee). Y es un dolor de cabeza combinarlos formalmente, aunque se usa con bastante facilidad por las criaturas borrosas que son los humanos, por ejemplo en la figura francesa del habla: synecdocque. :)

+0

Su respuesta es más que excelente, ¿por qué no puedo agregar una instancia de A a una Colección , aunque A es de hecho del tipo Able ... ¿Dónde está el tipo no seguro aquí? Thnx :) – elmorabea

8

De http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html:

There is, as usual, a price to be paid for the flexibility of using wildcards. That price is that it is now illegal to write into [a wildcard-based container]. For instance, this is not allowed:

public void addRectangle(List<? extends Shape> shapes) { 
    shapes.add(0, new Rectangle()); // Compile-time error! 
} 

You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is ? extends Shape -- an unknown subtype of Shape . Since we don't know what type it is, we don't know if it is a supertype of Rectangle ; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.

0

No se puede insertar cualquier objeto de cualquier tipo en una colección declara usando el comodín '?'

sólo puede insertar “nulo”

Una vez que se declara una colección como la lista, el compilador no puede saber que es seguro para añadir un SubAble.

¿Qué pasa si un Collection<SubSubAble> ha sido asignado a Collection<Able>? Esa sería una tarea válida, pero agregar un SubAble contaminaría la colección.

How can elements be added to a wildcard generic collection?

+0

"¿Qué pasa si se ha asignado una colección a SubAble?" Qué significa eso? –

+0

lo siento, es necesario marcarlos como sección de código; de lo contrario, no se muestran las partes genéricas – fmucar

0

declaración de

Map<String,? extends Able> as; 

significa "cualquier mapa, con claves de cadena, y los valores son del subtipo Capaz". Así, por ejemplo, puede hacer lo siguiente:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>(); 

Y ahora vamos a ver en este ejemplo:

Map<String,? extends Able> as = new HashMap<String, SubSubAble>(); 
as.put("key", new A()); 

Si fuera correcta, podrás terminar de tener HashMap con contenidos { "clave", nuevo A()} - que es tipo-error!

0

Collection<?> es el supertipo para todo tipo de colección. No es una colección que puede contener cualquier tipo. Al menos esa fue mi incomprensión de todo el concepto.

Podemos utilizarlo en el que no importa el tipo genérico, como en este ejemplo:

public static void print(Collection<?> aCollection) { 
    for (Object o:aCollection) { 
    System.out.println(o); 
    } 
} 

Si hubiéramos elegido la firma en su lugar:

public static void print(Collection<Object> aCollection) 

que lo haría nos hemos limitado a colecciones del tipo Collection<Object> - en otras palabras, dicho método no aceptaría un valor de tipo Collection<String>.

Así que es un tipo Collection<?>no una colección que puede tomar cualquier tipo . Solo toma el tipo desconocido.Y como no conocemos ese tipo (es desconocido;)), nunca podemos agregar un valor, porque ningún tipo en java es una subclase del tipo desconocido.

Si añadimos límites (como <? extends Able>), el tipo es todavía desconocido.

Está buscando una declaración de un mapa, cuyos valores implementan todos la interfaz Able. La declaración correcta es simplemente:

Map<String, Able> map; 

Supongamos que tenemos dos tipos A y B esa subclase Able y dos mapas adicionales

Map<String, A> aMap; 
Map<String, B> bMap; 

y quieren un método que devuelve ningún mapa cuyos valores implementar el Able interfaz: luego usamos el comodín:

public Map<String, ? extends Able> createAorBMap(boolean flag) { 
return flag ? aMap: bMap; 
} 

(nuevamente con la restricción de que no podemos agregar nuevos pares de clave/valor al mapa devuelto por este método).

3

Una buena manera de entender el problema es leer lo que significa el comodín:

Map<String,? extends Able> as; 

"Un mapa con las teclas de tipo String y los valores de un tipo que se extiende Capaz".

La razón por la que no se permiten las operaciones de adición es porque "abren la puerta" para introducir diferentes tipos en la colección, lo que entraría en conflicto con el sistema de escritura. p.

class UnAble implements Able; 
Map<String,UnAble> unableMap = new HashMap<String,UnAble>(); 
Map<String,? extends Able> ableMap = unableMap; 
ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map 

Un uso correcto de la construcción del comodín sería:

Result processAble(Map<String,? extends Able>) { ... read records & do something ... } 

Map<String,A> ableMap = new HashMap<String,A>; 
ableMap.put("willwork",new A()); 
processAble(as); 
processAble(unableMap); // from the definition above