2013-06-12 14 views
5

Ich habe paar geliefert SchnittstellenZuweisen einer Unterklasse einer generischen Klasse in eine Superklasse dieser Klasse

public interface Finder<S extends Stack<T>,T extends Item> { 
    public S find(S s, int a); 
} 

public interface Stack<T extends Item> { 
    Stack<T> getCopy(); 
} 

und eine Klasse, die die erste implementiert:

Wenn ich es nicht ändern kann Schnittstelle was wäre die beste Vorgehensweise, während die Implementierung so allgemein wie möglich gehalten wird?

EDIT Einige andere Code, den ich nicht brechen kann instanziiert SimpleFinder<ClassA,ClassB> so sollte ich auch zwei generische Typen bei der Umsetzung haben.

+0

Beste Vorgehensweise, um was zu erreichen? Warum können Sie die Schnittstellendefinitionen nicht ändern? – Bobulous

+0

versuchen Brutecast. Es gibt Möglichkeiten, es zu lösen (rekursiver Typ Parameter), aber es ist es nicht wert. – ZhongYu

+0

@Arkanon weil es nicht meins ist, sich zu ändern ...: | und rohe Besetzung ist ziemlich hässlich, nicht wahr? – SadStudent

Antwort

3

werfen Das Problem ist, dass offensichtlich Stack<T> nicht S extends Stack<T> ist. Java ist stark typisiert und lässt Sie solche Dinge nicht tun.

Sie können entweder auf Stack<T> umwandeln. In diesem Fall erhalten Sie weiterhin eine Warnung über die ungeprüfte Konvertierung. Dies bedeutet, dass diese Konvertierung unsicher ist.

public class SimpleFinder<S extends Stack<T>, T extends Item> implements Finder<S, T> { 

    @Override 
    public S find(S s, int a) { 
     Stack<T> stack = s.getCopy(); 
     return (S) stack; 
    } 

} 

oder verwenden Sie einfach Stack<T> statt S extends Stack<T>, das ist meine Empfehlung:

public class SimpleFinder<T extends Item> implements Finder<Stack<T>, T> { 

    @Override 
    public Stack<T> find(Stack<T> s, int a) { 
     Stack<T> stack = s.getCopy(); 
     return stack; 
    } 

} 
+0

Danke! und ja, mir ist der Grund klar, warum es nicht funktioniert Ich war einfach nicht sicher, wie ich das lösen könnte, ohne die Allgemeinheit zu verlieren: Der Grund, warum ich die zweite Lösung nicht machen kann, ist, dass es einen anderen Code gibt, der nicht mein ist SimpleFinder und ich kann es nicht brechen ... – SadStudent

+0

Ich könnte um zwei Argumente bitten und senden ', T>' wie Sie vorgeschlagen, um die Schnittstelle, aber dann wäre 'S' überflüssig und das ist irgendwie hässlich auch, nicht wahr? – SadStudent

+2

Die ungeprüfte Darstellungsoption ist unsicher - ich würde empfehlen, sie aus der Antwort zu entfernen oder am wenigsten einen aussagekräftigeren Haftungsausschluss hinzuzufügen. –

0
public class SimpleFinder<S extends Stack<T>,T extends Item> implements Finder<S,T>{ 
    public S find(S s, int a){ 
     Stack<T> stack = ....; 
     ... 
     stack = s.getCopy(); 
     ..... 
     return (S) stack; 
    } 
} 

sollte funktionieren. Denken Sie daran, dass der Stack Stack<T> und nicht S sein muss, damit er dem Rückgabetyp getCopy() entspricht. Ich würde erwarten, S Typ Ok sein, da es Stack<T> erweitert, aber die Umsetzung ist dies das Verhalten, das ich beobachte.

+0

Das kompiliert nicht ... – m0skit0

+0

Sorry, ich habe gerade editiert, ich hoffe es geht jetzt ok. –

+0

Nope kompiliert immer noch nicht: "Rückgabetyp für die Methode fehlt". Dieser Rückgabetyp ist nicht gültig (bis zu Java 7). – m0skit0

0

Ihre Art S ist ein Subtyp von Stack<T> aber die Kopiermethode ist Upcasting es zu einem Stack<T>, dass jeder Subtyp von Stack<T> sein kann. Sie müssen das Ergebnis der Kopie auf S

2

Da Sie nicht die Schnittstellen ändern können, haben Sie keine andere Wahl, als brutale Besetzung zu tun.

In einer allgemeineren Diskussion, was wir hier brauchen, ist "Selbst-Typ", möchten wir sagen, dass eine Methode Aufruf foo.bar() den statischen Typ von foo zurückgeben sollte. Normalerweise wird der Selbst-Typ für eine fließende API gewünscht, wobei die Methode foo selbst zurückgeben soll. In Ihrem Fall möchten Sie ein neues Objekt zurückgeben.

In Java gibt es keine befriedigende Antwort für den Selbsttyp. Ein Trick ist durch selbst referenziert Typ Parameter wie Foo<T extends Foo<T>>, aber es ist sehr hässlich, und es kann nicht wirklich erzwingen, dass jeder Subtyp Bar muss ein Foo<Bar> sein. Und der Trick wird in Ihrem Fall überhaupt nicht helfen.

kann Ein weiterer Trick

public interface Stack<T extends Item> { 
    <X extends Stack<T>> X getCopy(); 
} 

hier arbeiten, liefert der Anrufer die genaue Rückgabetyp.

 S stack = ....; 
    ... 
    stack = s.getCopy(); 
    // compiles, because X is inferred to be S 

Dieser Trick hilft, Call-Sites zu vereinfachen.Brute Casts existieren jedoch immer noch, versteckt in Implementierungen von getCopy(). Dieser Trick ist gefährlich und der Anrufer muss wissen, was er tut. Persönlich würde ich es nicht tun; Es ist besser, wenn der Anrufer den Cast ausführt.

1

Wie in den Kommentaren diskutiert, Ihr Design erfordert, dass die getCopy Methode der „self-Typ“ zurückkehren - das heißt, eine BlueStack<T> Umsetzung zu erwarten wäre eine BlueStack<T> von seinem getCopy zurückzukehren, und RedStack<T> sollte zurückgeben RedStack<T> usw.

Leider gibt es keine Möglichkeit, den "Selbsttyp" in Java auszudrücken. Als zhong.j.yu points out kommt ein rekursiven Parameter schließen, zum Beispiel:

//not recommended! 
public interface Stack<S extends Stack<S, T>, T extends Item> { 
    S getCopy(); 
} 

Aber wie zhong.j.yu erwähnt dies ist nicht intuitiv und würde immer noch ein BlueStack<T> von „liegend“ zu verhindern, scheitern und Rückkehr a RedStack<T> von seiner getCopy.

Stattdessen empfehle ich ein Redesign. Versuchen Sie, die Verantwortung für das Kopieren von Stapeln vom Typ Stack selbst zu entkoppeln. Zum Beispiel:

public interface StackCopier<S extends Stack<T>, T extends Item> { 

    S copy(S original); 
} 

Wenn Implementierungen von StackCopier benötigen Zugriff auf private Mitglieder ihrer jeweiligen Stack s, betrachten sie verschachtelte Klassen, zum Beispiel machen:

class BlueStack<T extends Item> implements Stack<T> { 

    ... 

    static class Copier<T extends Item> implements StackCopier<BlueStack<T>, T> { 

     @Override 
     public BlueStack<T> copy(BlueStack<T> original) { 

      ... 
     } 
    } 

Natürlich SimpleFinder werden müssten geändert entweder ein StackCopier<S, T> Feld oder nehmen Sie einen als neuen Parameter von find:

private final StackCopier<S, T> copier = ...; 

public S find(S stack, int a) { 

    S stackCopy = copier.copy(stack); 

    ... 

    return stackCopy; 
} 
+0

sogar dachte ich IMHO ein Redesign ist leider erforderlich, wie ich in der Frage erwähnt, kann ich die Schnittstelle nicht ändern, nur um es zu implementieren, so denke ich, ich bin stecken mit einem hässlichen Besetzung, aber ich denke, Ihre Lösung ist sehr nett ... – SadStudent

Verwandte Themen