Ich versuche eine abstrakte Klasse zu definieren, die Comparable implementiert. Wenn ich die Klasse mit folgender Definition definiere:
public abstract class MyClass implements Comparable <MyClass>
Unterklassen müssen implementiert werden compareTo(MyClass object)
. Stattdessen möchte ich, dass jede Unterklasse implementiert compareTo(SubClass object)
wird und ein Objekt ihres eigenen Typs akzeptiert. Wenn ich versuche, die abstrakte Klasse mit etwas zu definieren wie:
public abstract class MyClass implements Comparable <? extends MyClass>
Es wird beanstandet, dass "ein Supertyp keinen Platzhalter angeben darf".
Gibt es eine Lösung?
Comparable
direkte Implementierung . Niemand hält dich davon abMyImpl2 implements Comparable<MyImpl1>
.public abstract class MyClass<T> implements Comparable<T>
ist genauso gutAbgesehen von den mechanischen Schwierigkeiten, auf die Sie beim Deklarieren der Signaturen stoßen, macht das Ziel wenig Sinn. Sie versuchen, eine kovariante Vergleichsfunktion einzurichten, die die gesamte Idee der Einrichtung einer Schnittstelle, die abgeleitete Klassen anpassen können, zunichte macht.
Wenn Sie eine Unterklasse
SubClass
so definieren, dass ihre Instanzen nur mit anderenSubClass
Instanzen verglichen werden können , wie wird dannSubClass
der durch definierte Vertrag erfülltMyClass
? Denken Sie daranMyClass
, dass es und alle daraus abgeleiteten Typen mit anderenMyClass
Instanzen verglichen werden können . Sie versuchen , denn das ist nicht wahr zu machenSubClass
, was bedeutet , dassSubClass
nicht nicht erfülltMyClass
ist Vertrag: Sie können nicht ersetzen ,SubClass
fürMyClass
, weilSubClass
's Anforderungen strenger sind.Dieses Problem konzentriert sich auf Kovarianz und Kontravarianz und darauf, wie sich Funktionssignaturen durch Typableitung ändern können. Sie können sich entspannen eine Anforderung auf ein Argument der Typ Annahme einer breiteren Typ als die Supertyp Unterschrift Anforderungen-und Sie können stärken eine Anforderung auf einem Rückgabetyp versprechende eine schmalere Typ zurückzukehren als die Supertyp Unterschrift. Jede dieser Freiheiten erlaubt immer noch eine perfekte Substitution des abgeleiteten Typs für den Supertyp; Ein Anrufer kann den Unterschied nicht erkennen, wenn er den abgeleiteten Typ über die Schnittstelle des Supertyps verwendet, aber ein Anrufer, der den abgeleiteten Typ konkret verwendet, kann diese Freiheiten nutzen.
Willis Antwort lehrt etwas über generische Deklarationen, aber ich fordere Sie auf, Ihr Ziel zu überdenken, bevor Sie die Technik auf Kosten der Semantik akzeptieren.
quelle
List<MyClass>
, anstattList<MyClass<?>>
. Und wie würde es heißen, wenn Sie eines dieser Objekte bekommen und anrufenequals(anObject)
?compareTo()
Definition von Unterklassen zu erzwingen . MitEnum
wird diese Definition für abgeleitete Typen bereitgestellt - Typen, die der Compiler für Sie generiert - und das ist eine ungewöhnliche Situation. In Ihrem Fall stellen Sie nichts anderes als eine Verpflichtung für abgeleitete Typen bereit. Ist die Absicht zu garantieren, dass Sie generische Funktionen gegen Typ schreiben könnenMyType<T>
und sicher sein, dass es bietetcompareTo(MyType<T>)
? Wenn ja, könnten Sie das nicht auch tun, indem Sie Ihre Funktionsanforderung haben<T extends MyType && Comparable<T>>
?siehe Javas eigenes Beispiel:
public abstract class Enum<E extends Enum<E>> implements Comparable<E> public final int compareTo(E o)
zu sehs kommentar: normalerweise ist das argument richtig. Generika machen jedoch Typbeziehungen komplizierter. Eine Unterklasse ist möglicherweise kein Subtyp von MyClass in Willis Lösung.
SubClassA
ist ein Subtyp vonMyClass<SubClassA>
, aber kein Subtyp vonMyClass<SubClassB>
Typ
MyClass<X>
definiert einen Vertrag, fürcompareTo(X)
den alle seine Untertypen eingehalten werden müssen. Da gibt es kein Problem.quelle
Comparable
Facette derEnum
Schnittstelle darum, was eine bestimmte Unterklasse mit sich selbst (oder vielmehr mit Instanzen von sich selbst) tun kann, und nichtEnum
darum , was sie mit abgeleiteten Typen im Allgemeinen tun kann . Wenn es das ist, wonach Cem gesucht hat, dann halte ich Ihre Antwort für angemessener als meine.enum A
Testamentimplement Enum<A>
), die für benutzerdefinierte Klassen im Allgemeinen nicht gilt.Ich bin mir nicht sicher, ob Sie die Erfassung benötigen:
Fügen Sie zunächst compareTo zur abstrakten Klasse hinzu ...
public abstract class MyClass implements Comparable <MyClass> { @Override public int compareTo(MyClass c) { ... } }
Fügen Sie dann die Implementierungen hinzu ...
public class MyClass1 extends MyClass { ... } public class MyClass2 extends MyClass { ... }
Wenn Sie compare aufrufen, wird die Super-Type-Methode aufgerufen ...
MyClass1 c1 = new MyClass1(); MyClass2 c2 = new MyClass2(); c1.compareTo(c2);
quelle
compareTo
inMyClass1
oderMyClass2
mit einem anderen Parametertyp implementieren ?public abstract class MyClass<T> implements Comparable<T> { } public class SubClass extends MyClass<SubClass> { @Override public int compareTo(SubClass o) { // TODO Auto-generated method stub return 0; } }
quelle
Eine andere Lösung gefunden:
Die Lösung sollte folgendermaßen aussehen:
public abstract class MyClass implements ComparableFoo,Comparable<ComparableFoo> { public int compareTo(ComparableFoo o) { // your implementation } }
Diese Lösung impliziert, dass möglicherweise mehr Dinge ComparableFoo implementieren - dies ist wahrscheinlich nicht der Fall, aber dann codieren Sie in eine Schnittstelle und der generische Ausdruck ist einfach.
quelle
Ich weiß, dass Sie gesagt haben, Sie möchten "compareTo (SubClass-Objekt), das ein Objekt seines eigenen Typs akzeptiert", aber ich schlage trotzdem vor, die abstrakte Klasse wie folgt zu deklarieren:
public abstract class MyClass implements Comparable <Object>
Führen Sie eine Überprüfung durch, wenn Sie compareTo in MySubClass überschreiben:
@Override public int compareTo(Object o) { if (o instanceof MySubClass)) { ... } else throw new IllegalArgumentException(...) }
ähnlich wie "gleich" oder "klonen"
quelle