Entendiendo la palabra clave genérica JAVA 'super'

7

Recientemente, algunas personas dijeron que estaba equivocado cuando expliqué por qué no se puede insertar un objeto Number en una lista declarada como List<? super RationalNumber> . (Esto supone que RationalNumber es una subclase de Number )

Mi explicación fue que en List<? super RationalNumber> <? super RationalNumber> representa una superclase desconocida de RationalNumber . Esto significa que <? super RationalNumber> indica que un objeto válido para List<? super RationalNumber> es un objeto que extiende una superclase desconocida de RationalNumber .

Dado que una súper clase desconocida de RationalNumber es realmente desconocida (por ejemplo, puede ser una interfaz X en lugar de Number ), no se garantiza que Number sea una subclase de X ie implementa X , por lo que Number no es una sustitución válida de <? super RationalNumber> .

¿Qué hay de malo con esta explicación? Edición: Tenga en cuenta que estoy preguntando por qué problema hay con la forma en que lo explico, mi explicación. No otra explicación para la pregunta original.

Gracias.

    
pregunta InformedA 04.07.2014 - 08:39

2 respuestas

7

List<? super RationalNumber> es una lista de algún tipo X que es una superclase de RationalNumber. Su suposición es que RationalNumber es una subclase de Number , por lo que los posibles candidatos para X son RationalNumber , Number o Object . La posibilidad más restrictiva es que X es RationalNumber en sí mismo. Un List<RationalNumber> puede contener solo instancias de RationalNumber y no puede contener instancias de Number .

En resumen, un List<? super T> es una lista que puede aceptar instancias de T, pero el contenido actual es de tipo desconocido. Esto contrasta con List<? extends T> , que contiene instancias de T, pero no permite agregar nuevos elementos porque se desconoce el tipo aceptado.

Este ejemplo ilustra el uso de super :

void storePies(List<? super RationalNumber> list) { 
    list.add(new RationalNumber(22, 7))
    list.add(new RationalNumber(355, 113))
}

...
List<Number> list = new ArrayList<Number>();
storePies(list);
list.add(3.14159265358979);

Este ejemplo ilustra el uso de extends :

double sum(Collection<? extends Number> numbers) {
    double s = 0;
    for (Number n: numbers) { s += n.doubleValue(); }
    return s;
}

List<Integer> somePrimes = new ArrayList<Integer>();
somePrimes.add(5);
somePrimes.add(17);
double total = sum(somePrimes);
    
respondido por el kevin cline 04.07.2014 - 09:08
5

Ver también:

Una digresión. Siéntase libre de saltar.

Para los programadores que también están familiarizados con C #, existe una característica similar:

Los términos, covarianza y contravarianza, se remontan a las discusiones iniciales que surgen del Principio de sustitución de Liskov (LSP) .

El objetivo de diseño del comodín de límite inferior (LBWC) es garantizar el tipo de seguridad del siguiente código.

(Este es solo un ejemplo; uno de los más simples, entre otros.)

List<? super RationalNumber> containerOne = new ArrayList<RationalNumber>(); 
List<? super RationalNumber> containerTwo = new ArrayList<Number>(); 
List<? super RationalNumber> containerThree = new ArrayList<Object>(); 

RationalNumber itemValue = new RationalNumber(22, 7); 

containerOne.add(itemValue); 
containerTwo.add(itemValue); 
containerThree.add(itemValue);

En resumen, LBWC permite que las "handles" - clases genéricas que tienen un LBWC ( containerOne , containerTwo , containerThree en el ejemplo anterior) para :

  • Se asignarán instancias de la clase genérica (del tipo de parámetro T ) siempre que se sepa que RationalNumber se puede convertir a T con seguridad.

Ahora, usamos este conocimiento y tratamos de responder a la pregunta: ¿podemos pasar un Number a un "manejo" - una clase genérica que tiene un LBWC de RationalNumber ?

Dado que

  • Es válido asignar una instancia de new List<RationalNumber>() a un identificador de List<? super RationalNumber> varGeneric .
  • Por supuesto, es válido hacer algo completamente diferente; pero el diseño de LBWC es que esta es la situación con la que debe lidiar.

Obtenemos esta pregunta corolario:

  • ¿Podemos llamar a new List<RationalNumber>().add(new Number(...)) ?
    • Por supuesto que no.

Al ver que el objetivo de diseño de LBWC es evitar que esto suceda, se puede llegar a esta conclusión:

  • Si LBWC está diseñado para ser ejecutable (por el compilador o la policía de idiomas), debe evitar esta asignación (pasar un Number para un tipo ? para el cual el LBWC es RationalNumber ) desde sucediendo.

Esto responde a la pregunta desde una perspectiva de diseño. La cuestión de la aplicación solo puede ser respondida por miembros de la comunidad que estén familiarizados con la construcción del lenguaje Java y sus herramientas de compilación. Los comentarios de la multitud general solo proporcionarán especulaciones, o "hipótesis" o "teorías".

    
respondido por el rwong 04.07.2014 - 09:54

Lea otras preguntas en las etiquetas