sencillos programacion piezas numerico juegos codigo algoritmo java generics bounded-wildcard

programacion - Java Generics Puzzler, ampliando una clase y usando comodines



puzzle de 8 piezas java (6)

Está utilizando un comodín acotado ( TbinList<? extends Base>> ... ). Este comodín le impedirá agregar elementos a la lista. Si desea más información, aquí está la sección sobre Wildcards en los documentos.

He estado golpeándome la cabeza contra este por un tiempo y pensé que quizás algunos ojos nuevos verán el problema; gracias por tu tiempo.

import java.util.*; class Tbin<T> extends ArrayList<T> {} class TbinList<T> extends ArrayList<Tbin<T>> {} class Base {} class Derived extends Base {} public class Test { public static void main(String[] args) { ArrayList<Tbin<? extends Base>> test = new ArrayList<>(); test.add(new Tbin<Derived>()); TbinList<? extends Base> test2 = new TbinList<>(); test2.add(new Tbin<Derived>()); } }

Usando Java 8. Me parece que la creación directa del contenedor en test es equivalente al contenedor en test2 , pero el compilador dice:

Test.java:15: error: no suitable method found for add(Tbin<Derived>) test2.add(new Tbin<Derived>()); ^

¿Cómo escribo Tbin y TbinList para que la última línea sea aceptable?

Tenga en cuenta que en realidad Tbin s escritos, por lo que especifiqué Tbin<Derived> en la última línea.


Esto sucede debido a la forma en que funciona la conversión de captura :

Existe una conversión de captura de un tipo parametrizado G<T 1 ,...,T n > a un tipo parametrizado G<S 1 ,...,S n > , donde, para 1 ≤ i ≤ n :

  • Si T i es un argumento de tipo comodín del formulario ? extends B i ? extends B i , entonces S i es una variable de tipo nuevo [...].

La conversión de captura no se aplica de forma recursiva.

Tenga en cuenta el bit final. Entonces, lo que esto significa es que, dado un tipo como este:

Map<?, List<?>> // │ │ └ no capture (not applied recursively) // │ └ T2 is not a wildcard // └ T1 is a wildcard

Solo se capturan los comodines "externos". Se captura el comodín de la clave del Map , pero no se utiliza el comodín del elemento List . Es por eso que, por ejemplo, podemos agregar a una List<List<?>> , pero no a una List<?> . La ubicación del comodín es lo que importa.

Al llevar esto a TbinList , si tenemos una ArrayList<Tbin<?>> , el comodín está en un lugar donde no se captura, pero si tenemos una TbinList<?> , El comodín está en un lugar donde se obtiene capturado

Como mencioné en los comentarios, una prueba muy interesante es esta:

ArrayList<Tbin<? extends Base>> test3 = new TbinList<>();

Obtenemos este error:

error: incompatible types: cannot infer type arguments for TbinList<> ArrayList<Tbin<? extends Base>> test3 = new TbinList<>(); ^ reason: no instance(s) of type variable(s) T exist so that TbinList<T> conforms to ArrayList<Tbin<? extends Base>>

Por lo tanto, no hay forma de que funcione como está. Una de las declaraciones de clase necesita ser cambiada.

Además, piénsalo de esta manera.

Supongamos que tenemos:

class Derived1 extends Base {} class Derived2 extends Base {}

Y dado que un comodín permite el subtipo, podemos hacer esto:

TbinList<? extends Base> test4 = new TbinList<Derived1>();

¿Deberíamos poder agregar un Tbin<Derived2> a test4 ? No, esto sería una gran contaminación. Podríamos terminar con Derived2 s flotando en una TbinList<Derived1> .


OK, esta es la respuesta:

import java.util.*; class Tbin<T> extends ArrayList<T> {} class TbinList<T> extends ArrayList<Tbin<? extends T>> {} class Base {} class Derived extends Base {} public class Test { public static void main(String[] args) { TbinList<Base> test3 = new TbinList<>(); test3.add(new Tbin<Derived>()); } }

Como esperaba, algo obvio una vez que lo vi. Pero muchas sacudidas para llegar hasta aquí. Los genéricos de Java parecen simples si solo observa el código de trabajo.

Gracias a todos por ser una caja de resonancia.


Puede definir los tipos genéricos de la siguiente manera:

class Tbin<T> extends ArrayList<T> {} class TbinList<K, T extends Tbin<K>> extends ArrayList<T> {}

Entonces crearías una instancia como:

TbinList<? extends Base, Tbin<? extends Base>> test2 = new TbinList<>(); test2.add(new Tbin<Derived>());


Reemplazar la definición de TbinList con

class TbinList<T> extends ArrayList<Tbin<? extends T>> {}

y definiendo test2 con

TbinList<Base> test2 = new TbinList<>();

en cambio resolvería el problema.

Con su definición, está terminando con una ArrayList<Tbin<T>> donde T es cualquier clase fija que extiende Base .


no puedes agregar ningún objeto a TbinList<? extends Base> TbinList<? extends Base> , no se garantiza qué objetos está insertando en la lista. Se supone que lee los datos de test2 cuando usa comodines extends

Si declaraste como TbinList<? extends Base> TbinList<? extends Base> que significa que es cualquier subclase de la clase Base o de la clase Base, y cuando lo inicializa usa un diamante que no sea el nombre de la clase concreta, hace que su test2 no sea obvia, lo que hace que sea más difícil saber qué objetos se pueden insertar . Mi sugerencia es que evite tal declaración, es peligroso, puede que no tenga errores de compilación pero es un código horrible, puede agregar algo, pero también puede agregar algo INCORRECTO que romperá su código.