2012-05-03 13 views
7

me gustaría escribir un Interator así:Generifying con "super"

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 parece que vuelve árboles o plantas (a pesar de que son siempre los 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).

+0

¿estoy leyendo esto mal? y luego 'private List list' y' public T next() {T maple = list.get (index ++); devolver arce;) ' – DefyGravity

Respuesta

3

Si Maple es Tree y Plant, porque extiende ambos, ¿cuál es el punto en la cláusula súper que desea utilizar? Puede, según el polimorfismo de subtipo clásico, asignar Mapple a Object, Tree o 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 pretendes hacer es definir el límite de un argumento de tipo no del tipo. Simplemente puede declarar su parámetro de tipo como T, sin límites, y luego, cuando lo usa, usa un comodín como argumento de tipo.

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

Y cuando lo utiliza:

MyIterator<? super Maple> iter; 
0

Se declara un parámetro de tipo -bounded super no está permitido, y the reasoning is that it is not useful.

que haya logrado tropezar en un caso divertido, donde, además, se hace tiene sentido, porque si va a implementar un iterador de sólo lectura, se puede ser conveniente atado así.

La opción simple es dejarlo estar e implementar Iterator<Maple>, o Iterator<T> con alguna comprobación de tipo.

1

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

Supongamos (por un momento) que el iterador tiene un método insertInto (T t) que coloca un objeto en la lista en el punto donde está el iterador. . Si lanza el iterador como Iterator <Plant>, 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 tratar de poner una zanahoria en una lista subyacente de arces.

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

0

Aparentemente, no existe una forma ideal de hacerlo, por lo que sugiero dos soluciones por debajo de lo óptimo.

El primero es un iterador envoltorio así:

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 retorno de esta manera:

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

Esto proporciona seguridad de tipos completa a expensas de la creación de una nueva objeto.

Una alternativa es utilizar 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 envoltorio, pero a expensas de todo tipo de seguridad.