2012-05-03 9 views
7

Ich mag würde ein Interator wie folgt schreiben:Generifying mit „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. 
} 

Konzeptionell ist die Idee, einen Iterator zu haben, wie es aussieht, Bäume oder Pflanzen gibt (obwohl sie immer Maples) ohne separate Klassen für jedes zu schreiben.

Aber der Compiler mag es nicht, wenn ich mit T super Maple generalisieren; anscheinend können Sie nur eine Klasse mit T extends Something generifizieren. Kennt jemand einen guten Weg, dasselbe zu erreichen?

Meine Motivation zu fragen ist, dass ich ein Programm habe, das Schnittstellen für seine API verwendet. Ich möchte eine Methode haben, die einen Iterator von Schnittstellen zurückgibt (für die API), und eine andere, die einen Iterator der Implementierungsklassen zurückgibt (zur internen Verwendung).

+0

lese ich das falsch? und dann 'private List list' und' public T next() {T maple = list.get (index ++); Ahorn zurückgeben;) ' – DefyGravity

Antwort

3

Wenn Maple ist ein Tree und ein Plant, weil es beide erweitert, was ist der Punkt dann in der Super-Klausel, die Sie verwenden möchten? Sie können dem klassischen Subtyp-Polymorphismus Mapple bis Object, Tree oder Plant Referenzen zuordnen.

Sowohl ? extends T als auch ? super T sind Platzhalter, die als Typargumente deklariert wurden, um einen Typparameter T zu ersetzen.

Sie beabsichtigen, die Grenze eines Typarguments nicht vom Typparameter zu definieren. Sie können Ihren Typparameter einfach als T ohne Begrenzungen deklarieren, und wenn Sie ihn dann verwenden, verwenden Sie einen Platzhalter als Typargument.

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

Und wenn Sie es verwenden:

MyIterator<? super Maple> iter; 
0

Es hat eine super -bounded Typ-Parameter erklärt nicht erlaubt ist, und the reasoning is that it is not useful.

Sie haben auf einem lustigen Fall stolpern geschaffen, wo, na ja, es tut Sinn machen, denn wenn Sie eine schreibgeschützte Iterator Implementierung, es zu gebunden bequem sein kann, es so.

Die einfache Option ist es einfach zu lassen und implementieren Iterator<Maple> oder Iterator<T> mit einigen Typprüfungen.

1

Ok, also gibt Ihr Iterator einen Iterator über Maple zurück, aber Sie wollen ihn als Iterator über Plant tarnen.

Lassen Sie uns (für einen Moment) vorgeben, dass Iterator eine Methode insertInto (T t) hatte, die ein Objekt in die Liste an dem Punkt bringt, an dem sich der Iterator befindet. . Wenn Sie den Iterator als Iterator <Plant> umsetzen, dann würde das bedeuten, dass es i.insertInto (myCarrot) heißt - weil Karotten Pflanzen sind. Aber das würde die Typen verletzen - es würde versuchen, eine Karotte in eine zugrunde liegende Liste von Ahornbäumen zu stecken.

Mit anderen Worten, Ihr Iterator ist nicht ein Iterator über Pflanzen, es wird nicht nur irgendeine alte Pflanze als Methode Argumente nehmen, und deshalb können Sie es nicht als solche gießen oder generifizieren.

0

Offensichtlich gibt es keinen idealen Weg, dies zu tun, also werde ich zwei suboptimale Lösungen vorschlagen.

Der erste ist ein Wrapper Iterator wie folgt aus:

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 
} 

Auf diese Weise können Sie den Rückgabetyp wie folgt ändern: auf Kosten

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

Dies bietet komplette Typsicherheit eine neue zu schaffen, Objekt.

Eine Alternative ist, einen ungeprüften Guss zu verwenden:

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

Dadurch entfällt die Notwendigkeit, ein Wrapper-Objekt, aber auf Kosten aller Art Sicherheit zu schaffen.