Tengo una lista que se declara así:
List<? extends Number> foo3 = new ArrayList<Integer>();
Traté de agregar 3 a foo3. Sin embargo, recibo un mensaje de error como este:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
List<? extends Number>
no significa "lista de objetos de diferentes tipos, todos los cuales se extiendenNumber
". Significa "lista de objetos de un solo tipo que se extiendeNumber
".Respuestas:
Lo siento, pero no puedes.
La declaración comodín de
List<? extends Number> foo3
significa que la variablefoo3
puede contener cualquier valor de una familia de tipos (en lugar de cualquier valor de un tipo específico). Significa que cualquiera de estos son tareas legales:Entonces, dado esto, qué tipo de objeto podría agregar a
List foo3
eso sería legal después de cualquiera de las posiblesArrayList
asignaciones anteriores :Integer
porquefoo3
podría estar apuntando a unList<Double>
.Double
porquefoo3
podría estar apuntando a unList<Integer>
.Number
porquefoo3
podría estar apuntando a unList<Integer>
.No puede agregar ningún objeto
List<? extends T>
porque no puede garantizar a qué tipo deList
señal realmente está apuntando, por lo que no puede garantizar que el objeto esté permitido en esoList
. La única "garantía" es que solo puede leer de ella y obtendrá unaT
o subclase deT
.La lógica inversa se aplica a
super
, por ejemploList<? super T>
. Estos son legales:No puede leer el tipo específico T (p
Number
. Ej. )List<? super T>
Porque no puede garantizar a qué tipo deList
apunta realmente. La única "garantía" que tiene es que puede agregar un valor de tipoT
(o cualquier subclase deT
) sin violar la integridad de la lista a la que apunta.El ejemplo perfecto de esto es la firma de
Collections.copy()
:Observe cómo la
src
declaración de la lista usaextends
para permitirme pasar cualquier Lista de una familia de tipos de Lista relacionados y aún así garantizar que producirá valores de tipo T o subclases de T. Pero no puede agregar a lasrc
lista.La
dest
declaración de la listasuper
me permite pasar cualquier Lista de una familia de tipos de Lista relacionados y aún así garantizar que pueda escribir un valor de un tipo T específico en esa lista. Pero no se puede garantizar la lectura de los valores de tipo específico T si leo de la lista.Entonces, gracias a los comodines genéricos, puedo hacer cualquiera de estas llamadas con ese único método:
Considere este código confuso y muy amplio para ejercitar su cerebro. Las líneas comentadas son ilegales y la razón por la cual se indica en el extremo derecho de la línea (es necesario desplazarse para ver algunas de ellas):
fuente
src
List
argumento se usaextends
para leer de la lista src, mientras que eldest
List
argumento se usasuper
para escribir en la lista de destino. Esto permite un método que puede copiar desdeList<Integer>
oList<Double>
haciaList<Number>
oList<Object>
.or subclass of T
es correcto. Por ejemplo, no puedo agregar un anObject
(superclase deNumber
)List<? super Number> foo3
porquefoo3
puede haber sido asignado como:List<? super Number> foo3 = new ArrayList<Number>
(que puede contener soloNumber
o subclases deNumber
).<? super Number>
se refiere a los tipos deList<>
s que se pueden asignarfoo3
, no a los tipos de cosas que se pueden agregar / leer de él. Los tipos de cosas que se pueden agregar / quitarfoo3
deben ser cosas que se pueden agregar / quitar de cualquier tipo de cosas a las que seList<>
pueda asignarfoo3
.No puedes (sin yesos inseguros). Solo puedes leer de ellos.
El problema es que no sabes de qué es exactamente la lista. Podría ser una lista de cualquier subclase de Número, por lo que cuando intentas poner un elemento en él, no sabes que el elemento realmente se ajusta a la lista.
Por ejemplo, la Lista puede ser una lista de
Byte
s, por lo que sería un error ponerle unaFloat
.fuente
"La lista '<'? Extiende Número> es en realidad un comodín de límite superior.
El comodín de límite superior dice que cualquier clase que amplíe Número o Número en sí puede usarse como el tipo de parámetro formal: el problema se debe al hecho de que Java no sabe qué tipo de Lista es realmente. Tiene que ser un tipo EXACTO y ÚNICO. Espero que ayude :)
fuente
Me ha resultado confuso aunque leí las respuestas aquí, hasta que encontré el comentario de Pavel Minaev:
Después de esto pude entender la increíble explicación de BertF. Lista <? extiende Número> medios? podría ser de cualquier tipo Número extendido (Entero, Doble, etc.) y no está aclarado en la declaración (Lista <? extiende Número> lista) cuál de ellos es, así que cuando quieres usar el método de agregar no se sabe si la entrada es del mismo tipo o no; ¿De qué tipo es?
Entonces, ¿los elementos de List <? extiende Número> solo se puede establecer al construir.
También tenga en cuenta esto: cuando usamos plantillas, le estamos diciendo al compilador con qué tipo estamos jugando. T, por ejemplo, tiene ese tipo para nosotros, pero no ? hace lo mismo
Tengo que decir ... Este es uno de los sucios para explicar / aprender
fuente
Podrías hacer esto en su lugar:
fuente
Puede evitarlo creando una referencia a la Lista con un tipo diferente.
(Estos son los "moldes inseguros" mencionados por sepp2k).
Porque
untypedList
,superclassedList
ytrulyclassedList
son solo referencias alist
, todavía estará agregando elementos a la ArrayList original.En realidad, no es necesario usarlo
(List<?>)
en el ejemplo anterior, pero puede que lo necesite en su código, dependiendo del tipo quelist
le hayan dado.Tenga en cuenta que el uso
?
le dará advertencias del compilador, hasta que ponga esto por encima de su función:fuente
donde extender la lista desde 'Objeto', puede usar list.add y cuando lo desee usar list.get solo necesita lanzar Object a su Object;
fuente