2010-09-15 7 views

Respuesta

36

Lo hace por seguridad. Imagínese si funcionaba:

List<Child> childList = new ArrayList<Child>(); 
childList.add(new Child()); 

List<? extends Parent> parentList = childList; 
parentList.set(0, new Parent()); 

Child child = childList.get(0); // No! It's not a child! Type safety is broken... 

El significado de List<? extends Parent> es "El es una lista de algún tipo que se extiende Parent No sabemos qué tipo - que podría ser un List<Parent>, un List<Child>, o una List<GrandChild>.. " Eso hace que sea seguro para ir a buscar a cabo ningún artículo de la API List<T> y convertir de T a Parent, pero no es seguro llamar en a la List<T> API de conversión de Parent a T ... porque que la conversión puede no ser válido.

+0

Mal ejemplo de polimorfismo, que pienso? Un "niño" no es (siempre) un "padre". – justhalf

+0

@justhalf: 'Parent' estaba en la pregunta, y sugiere que' Child' es un ejemplo natural de una subclase. No los nombres de clase que habría elegido desde cero, pero lo suficientemente claros en términos del contexto del que se trata la pregunta. –

+0

Sí, entiendo, pero creo que hubiera sido mejor si el ejemplo aquí utiliza "SingleParent" o "NewParent" como la subclase =) – justhalf

16
List<? super Parent> 

PECS - "Producer - Extends, Cosumer - Super". Su List es un consumidor de objetos Parent.

1

Aquí está mi comprensión.

Supongamos que tenemos un tipo genérico con 2 métodos

type L<T> 
    T get(); 
    void set(T); 

Supongamos que tenemos un super tipo P, y tiene subtipos C1, C2 ... Cn. (Por conveniencia decimos P es un subtipo de la misma, y ​​es en realidad uno de los Ci)

Ahora también conseguimos n tipos concretos L<C1>, L<C2> ... L<Cn>, como si hemos escrito manualmente n tipos:

type L_Ci_ 
    Ci get(); 
    void set(Ci); 

No tuvimos que escribirlos manualmente, ese es el punto. Hay no hay relaciones entre estos tipos

L<Ci> oi = ...; 
L<Cj> oj = oi; // doesn't compile. L<Ci> and L<Cj> are not compatible types. 

Para la plantilla de C++, que es el fin de la historia. Básicamente se trata de expansión macro, basada en una clase de "plantilla", genera muchas clases concretas, sin relaciones de tipo entre ellas.

Para Java, hay más. También conseguimos un tipo L<? extends P>, es un tipo súper de cualquier L<Ci>

L<Ci> oi = ...; 
L<? extends P> o = oi; // ok, assign subtype to supertype 

¿Qué clase de método debe existir en L<? extends P>? Como supertype, cualquiera de sus métodos debe estar oculto por sus subtipos. Este método funcionaría:

type L<? extends P> 
    P get(); 

porque en cualquiera de sus subtipo L<Ci>, hay un método Ci get(), que es compatible con P get() - el método predominante tiene la misma firma y tipo de retorno covariantes.

Esto no puede trabajar para set() sin embargo - no podemos encontrar un tipo X, de manera que void set(X) puede ser anulado por void set(Ci) para cualquier Ci. Por lo tanto, el método set() no existe en L<? extends P>.

También hay un L<? super P> que va en la dirección opuesta. Tiene set(P), pero no get(). Si Si es un tipo estupendo de P, L<? super P> es un tipo estupendo de L<Si>.

type L<? super P> 
    void set(P); 

type L<Si> 
    Si get(); 
    void set(Si); 

set(Si) "anulaciones" set(P) no en el sentido habitual, pero el compilador puede ver que cualquier invocación válida en set(P) es una invocación válida en set(Si)

+0

Se deniega una gran explicación sobre el motivo set() en este contexto. –

Cuestiones relacionadas