tl; dr
Der Versuch, eine hierarchisch fließende Schnittstelle zu implementieren, so dass ich Knoten untergeordnete Klassen kombinieren kann, während auch die Klasse standalone, aber get type Parameter ist nicht innerhalb seiner gebundenen Fehler.
Details
Ich versuche eine Lösung zu finden, damit ich etwas so erstellen kann, dass ich so etwas wie machen kann:
%Vor%während es auch möglich ist:
%Vor%Ich habe mich mit verschiedenen Strategien vertraut gemacht, konnte aber die beschriebene Flexibilität nicht erreichen.
Mein aktueller Versuch verwendet die folgenden Klassen:
%Vor%Die Fehler, die ich sehe, sind häufig:
Animal.java:4: type parameter Animal ist nicht innerhalb seiner gebundenen privaten Katze; Animal.java:5: type Parameter Animal ist nicht in seinem gebundenen privaten Hund Hund;
Angewendet auf alle ähnlichen Referenzen und auch auf meinen Beispiel-Wunschfall bezogen:
kann kein Symbol finden Symbol: Methode Hund () Ort: Klasse Base.dog ()
Ich habe versucht, die folgenden Lösungen zu verwenden, die ähnliche Probleme anzugehen schienen, aber ohne Erfolg, daher ist jede Unterstützung willkommen.
Referenzen
Der folgende Code scheint gut zu funktionieren und benötigt keine @SuppressWarnings
. Das Schlüsselkonzept, das Sie verstehen sollten, ist, dass Ihr T
-Parameter tatsächlich die Klasse des übergeordneten Objekts Ihres Objekts ist, aber das übergeordnete Element T
könnte alles sein. Also statt T extends Base<T>
willst du T extends Base<?>
.
Die Ausgabe ist:
%Vor% ... was ich für richtig halte, obwohl Sie vielleicht Ihre Dog.chacesCar()
-Methode ändern möchten, damit cat drinks milk
nicht ausgegeben wird! Außerdem sollte es chases
nicht chaces
sein.
Hoffe, das hilft!
%Vor%Testcode:
%Vor%Das Beste, was ich herausgefunden habe, ist folgendes:
%Vor%Mit der folgenden Basisklasse:
%Vor%Wenn Sie backRef ab Typ T deklarieren, sind die anderen Klassen nicht erlaubt, da sie keine Unterklassen voneinander sind. Sie müssen also einen anderen Typ angeben, aber da dieser Typ kontextabhängig ist (einmal sein Cat, Einmal sein Hund) Ich sehe keine Alternative, um einen Hinweis zu geben.
Ich habe eine Lösung gefunden:
%Vor%Wie jemand vorgeschlagen hat, fügen wir einen zusätzlichen Typ für den Eltern hinzu.
Jetzt die Basisklassen:
%Vor%Wie Sie sehen, legt Cat klar fest, welchen Basistyp es verwenden soll. Jetzt für den Menschen, der den Typ abhängig vom Kontext ändern kann:
%Vor%}
Human gibt ein zusätzliches generisches Element an, das der Aufrufer (Cat, Dog) in seiner Methode findHuman () angibt.
Das haben wir an einem unserer Projekte gemacht:
%Vor% %Vor%Wenn child eine eigene Unterklasse hätte, wäre dies:
%Vor%In dieser Zeile:
%Vor%Der Compiler behandelt den zweiten Typparameter als eine konkrete Klasse. Angenommen, Sie haben diese Zeile durch Folgendes ersetzt:
%Vor%'Double' ist eine konkrete Klasse. Wenn der Compiler dies durchsucht, kann er den Unterschied zwischen Ihrem T und Double nicht erkennen und behandelt sie beide als konkrete Klasse und nicht als Typ-Parameter. Die einzige Möglichkeit, dem Compiler mitzuteilen, dass T ein Typparameter ist, lautet folgendermaßen:
%Vor%Ich hoffe, dass dies Ihre Frage beantwortet (oder zumindest relevant ist).
Bearbeiten Post wurde bearbeitet, während ich tippte, also denke ich, dass diese Antwort nicht mehr relevant ist.
Ihr Problem ist, dass die Methode den Elternteil zurückgeben soll, aber der Elternteil ist nicht unbedingt ein T, sondern nur eine Basis. Und das andere Problem ist, dass die Methode done
immer die gleiche Klasse zurückgeben sollte, unabhängig von der Klasse.
Aber hier ist eine kleine Variation Ihrer vorgeschlagenen Klassen. Zuerst für Base
, das seine konkrete Klasse und ihr konkretes Elternteil deklariert:
Dadurch werden die abgeleiteten konkreten Klassen:
%Vor%Damit könnte ich ohne Fehler oder Warnung schreiben:
%Vor% Beachten Sie jedoch, dass ich die Aufrufe von done
durch animals
ersetzen musste.
Bearbeiten:
Ich habe eine neue Klasse CatOrDog
hinzugefügt, um die Human
Verarbeitung zu faktorisieren. Da das übergeordnete Element eines Human
ein Farm
ist, initialisiere ich das neue human
mit einem korrekten Elternelement, falls es existiert. Auf diese Weise kompiliert nicht nur die oben genannten Quellen ohne Fehler oder Warnung, sondern es läuft auch ohne Probleme und es wird gedruckt:
Es gibt keinen "sicheren" Weg, dies zu tun, aber dies sollte kompilieren:
%Vor%Sie können auch mit Interfaces spielen, so dass Sie mehrfache Vererbung fälschen können. Ein bisschen ausführlich, aber es gibt keine gefährliche Casting, und ich finde es ganz verständlich.
Definieren Sie die verfügbaren Methoden:
%Vor% Sie müssen möglicherweise mehrere Methoden in einer Schnittstelle haben, aber ich habe diesen Bedarf noch nicht erfüllt. Zum Beispiel, wenn Sie Arten von meow
s gehabt hätten:
Definieren Sie die Ausgabetypen:
Farm
liefert Animal
oder Human
:
Animal
liefert Cat
, Dog
oder Done
:
Cat
liefert Meow
, FindsHuman
oder Done
:
Dog
liefert Bark
, ChacesCar
, FindsHuman
oder Done
:
Human
liefert SayHello
oder Done
:
Implementieren Sie einfach die * Out-Schnittstellen:
%Vor% Diese Implementierungen würden auch ohne die Schnittstellen funktionieren: Entfernen Sie implements *Out
, @Override
s und ersetzen Sie alle *Out
durch *
(z. B. AnimalOut
by Animal
). Das heißt, es ist einfacher, mit den Schnittstellen zu warten: Aktualisieren Sie sie einfach und beheben Sie Ihre Kompilierungsfehler. Es ist auch einfacher, DSL-Lösungen mit Schnittstellen zu finden (wie Sie sehen können), und manchmal sind sie einfach notwendig.
Demo:
%Vor%Drucke:
%Vor%Tags und Links java generics nested-generics fluent-interface