Generando con "super"

Me gustaría escribir un Interator como este:

class Plant { }
class Tree extends Plant { }
class Maple extends Tree { }

// Iterator class: compiler error on the word "super".
class MyIterator<T super Maple> implements Iterator<T> {
    private int index = 0;
    private List<Maple> list = // Get the list from an external source.

    public T next() {
         Maple maple = list.get(index++);
         // Do some processing.
         return maple;
    }

    // The other methods of Iterator are easy to implement.
}

Conceptualmente, la idea es tener un iterador que parezca que devuelve árboles o plantas (aunque siempre sean arces) sin escribir clases separadas para cada uno.

Pero al compilador no le gusta cuando genero con T super Maple; aparentemente solo puedes generar una clase usando T extends Something. ¿Alguien sabe de una buena manera de lograr lo mismo?

Mi motivación para preguntar es que tengo un programa que usa interfaces para su API. Quiero tener un método que devuelva un iterador de interfaces (para la API) y otro que devuelva un iterador de las clases de implementación (para uso interno).

preguntado el 03 de mayo de 12 a las 21:05

¿Estoy leyendo esto mal? y luego private List<T> list y public T next(){ T maple = list.get(index++); return maple;) -

5 Respuestas

If Maple es un Tree y Plant, porque se extiende a ambos, ¿cuál es el punto entonces en la súper cláusula que desea usar? Puede, por polimorfismo de subtipo clásico asignar Mapple a Object, a un Tree o para Plant Referencias.

Ambos ? extends T y ? super T son comodines, declarados como argumentos de tipo para sustituir un parámetro de tipo T.

Lo que pretende hacer es definir el límite de un argumento de tipo, no del parámetro de tipo. Simplemente puede declarar su parámetro de tipo como T, sin límites, y luego, cuando lo use, use un comodín como argumento de tipo.

class MyIterator<T> implements Iterator<T> { ... }

Y cuando lo usas:

MyIterator<? super Maple> iter;

contestado el 03 de mayo de 12 a las 22:05

Ok, entonces su iterador devuelve un iterador sobre arce, pero desea lanzarlo como un iterador sobre Plant.

Supongamos (por un momento) que Iterator tiene un método insertInto(T t) que coloca un objeto en la lista en el punto donde se encuentra el iterador. . Si lanzas el iterador como Iterator entonces eso significaría que estaría bien llamar a i.insertInto(myCarrot), porque las zanahorias son plantas. Pero esto violaría los tipos: sería intentar poner una zanahoria en una lista subyacente de arces.

En otras palabras, su iterador es no un iterador sobre Plants, no tomará cualquier planta antigua como argumentos de método, y es por eso que no puede convertirla o generarla como tal.

contestado el 04 de mayo de 12 a las 03:05

es declarar un super-el parámetro de tipo acotado no está permitido, y el razonamiento es que no sirve.

Te las has arreglado para tropezar con un caso gracioso en el que, bueno, tiene sentido, porque si está implementando un iterador de solo lectura, puede conveniente encuadernarlo así.

La opción simple es simplemente dejarlo ser e implementar Iterator<Maple> o el Iterator<T> con alguna verificación de tipos.

contestado el 03 de mayo de 12 a las 21:05

Aparentemente, no hay una forma ideal de hacer esto, por lo que sugeriré dos soluciones subóptimas.

El primero es un iterador de contenedor como este:

public class SuperTypeIterator<E> implements Iterator<E> {
    private final Iterator<? extends E> iter;

    public SuperTypeIterator(Iterator<? extends E> iter) {
        this.iter = iter;
    }

    @Override
    public E next() {
        return iter.next();
    }

    // And similarly for the other methods of Iterator
}

Esto le permite cambiar el tipo de devolución de esta manera:

Iterator<Plant> getPlantIterator() {
    return new SuperTypeIterator<Plant>( new MapleIterator() );
}

Esto proporciona seguridad de tipos completa a expensas de crear un nuevo objeto.

Una alternativa es usar un elenco sin control:

Iterator<Plant> getPlantIterator() {
    return (Iterator<Plant>) (Iterator<?>) new MapleIterator();
    // Yes, the double cast is necessary to get it to compile.
}

Esto elimina la necesidad de crear un objeto contenedor, pero a expensas de la seguridad de todo tipo.

contestado el 07 de mayo de 12 a las 10:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.