Ich dachte, ich verstehe Java-Generika ziemlich gut, aber dann bin ich in java.lang.Enum auf Folgendes gestoßen:
class Enum<E extends Enum<E>>
Könnte jemand erklären, wie dieser Typparameter zu interpretieren ist? Bonuspunkte für die Bereitstellung anderer Beispiele dafür, wo ein ähnlicher Typparameter verwendet werden könnte.
Antworten:
Dies bedeutet, dass das Typargument für enum von einer Aufzählung abgeleitet werden muss, die selbst dasselbe Typargument hat. Wie kann das passieren? Indem Sie das Typargument zum neuen Typ selbst machen. Wenn ich also eine Aufzählung mit dem Namen StatusCode habe, entspricht dies:
Wenn Sie nun die Einschränkungen überprüfen, haben wir
Enum<StatusCode>
- alsoE=StatusCode
. Lassen Sie uns überprüfen:E
verlängertEnum<StatusCode>
? Ja! Wir sind okay.Sie fragen sich vielleicht, worum es geht :) Nun, das bedeutet, dass die API für Enum auf sich selbst verweisen kann - zum Beispiel, wenn Sie sagen können, dass dies
Enum<E>
implementiert istComparable<E>
. Die Basisklasse kann die Vergleiche durchführen (im Fall von Aufzählungen), aber sie kann sicherstellen, dass nur die richtige Art von Aufzählungen miteinander verglichen wird. (EDIT: Nun, fast - siehe die Bearbeitung unten.)Ich habe etwas Ähnliches in meinem C # -Port von ProtocolBuffers verwendet. Es gibt "Nachrichten" (unveränderlich) und "Builder" (veränderlich, zum Erstellen einer Nachricht verwendet) - und sie kommen als Typenpaare. Die beteiligten Schnittstellen sind:
Dies bedeutet, dass Sie aus einer Nachricht einen geeigneten Builder erhalten können (z. B. um eine Kopie einer Nachricht zu erstellen und einige Bits zu ändern), und von einem Builder können Sie eine entsprechende Nachricht erhalten, wenn Sie mit dem Erstellen fertig sind. Es ist ein guter Job, den Benutzer der API nicht wirklich interessieren müssen - es ist schrecklich kompliziert und es dauerte mehrere Iterationen, um dorthin zu gelangen, wo es ist.
BEARBEITEN: Beachten Sie, dass dies Sie nicht davon abhält, ungerade Typen zu erstellen, die ein Typargument verwenden, das selbst in Ordnung ist, aber nicht vom selben Typ ist. Der Zweck besteht darin, Vorteile im richtigen Fall zu bieten, anstatt Sie vor dem falschen Fall zu schützen .
Wenn
Enum
Sie also in Java sowieso nicht "speziell" behandelt würden, könnten Sie (wie in den Kommentaren angegeben) die folgenden Typen erstellen:Second
würdeComparable<First>
eher implementieren alsComparable<Second>
... aberFirst
selbst wäre in Ordnung.quelle
Enum
Es gibt keine Instanzmethoden, die den Typparametertyp zurückgeben.class Enum<E>
in allen Fällen ausreichend ist. Und in Generika sollten Sie eine restriktivere Bindung nur verwenden, wenn dies tatsächlich erforderlich ist, um die Typensicherheit zu gewährleisten.Enum
Subklassen der einzige Grund , nicht immer automatisch generiert wurden, müssen Sieclass Enum<E extends Enum<?>>
überclass Enum<E>
die Fähigkeit, Zugangordinal
zucompareTo()
. Wenn Sie jedoch darüber nachdenken, ist es aus sprachlicher Sicht nicht sinnvoll, zwei verschiedene Arten von Aufzählungen über ihre Ordnungszahlen zu vergleichen. Daher ist die ImplementierungEnum.compareTo()
dieser Verwendungenordinal
nur im Zusammenhang mit derEnum
automatischen Generierung von Unterklassen sinnvoll . Wenn Sie manuell Unterklasse könntenEnum
,compareTo
müsste wahrscheinlich seinabstract
.Das Folgende ist eine modifizierte Version der Erklärung aus dem Buch Java Generics and Collections : Wir haben eine
Enum
deklariertedie zu einer Klasse erweitert wird
wo
...
soll die irgendwie parametrisierte Basisklasse für Enums sein. Lassen Sie uns herausfinden, was das sein muss. Nun, eine der Anforderungen dafürSeason
ist, dass es implementiert werden sollteComparable<Season>
. Also werden wir brauchenWas könnten Sie dafür verwenden
...
, damit dies funktioniert? Da es sich um eine Parametrisierung von handeln mussEnum
, besteht die einzige Wahl darinEnum<Season>
, dass Sie Folgendes haben können:So
Enum
wird auf Typen wie parametriertSeason
. Wenn Sie von abstrahieren, erhaltenSeason
Sie, dass der Parameter vonEnum
ein beliebiger Typ ist, der erfülltMaurice Naftalin (Co-Autor, Java Generics and Collections)
quelle
Season
implementiertComparable<Season>
?compareTo
Methode muss also als Subtyp deklariert worden sein, sonstEnum
sagt der Compiler (korrekt), dass es keine Ordnungszahl gibt.Enum
hätte, wäre dies möglichclass OneEnum extends Enum<AnotherEnum>{}
, selbst wenn die derzeitigeEnum
Deklaration erfolgt. Es würde nicht viel Sinn in der Lage machen mit einer anderen Art von ENUM zu vergleichen, so ist, dannEnum
istcompareTo
nicht sinnvoll wie eh erklärt. Die Grenzen helfen dabei nicht weiter.public class Enum<E extends Enum<?>>
würde das auch ausreichen.Dies kann durch ein einfaches Beispiel und eine Technik veranschaulicht werden, mit der verkettete Methodenaufrufe für Unterklassen implementiert werden können. In einem Beispiel unten wird
setName
eine zurückgegeben,Node
sodass die Verkettung für Folgendes nicht funktioniertCity
:Wir könnten also in einer generischen Deklaration auf eine Unterklasse verweisen, sodass die
City
jetzt den richtigen Typ zurückgibt:quelle
return (CHILD) this;
Erwägen Sie das Hinzufügen einer getThis () -Methode:protected CHILD getThis() { return this; }
Siehe: angelikalanger.com/GenericsFAQ/FAQSections/…Node<T>
was nicht der Fall ist), ignoriere ich sie, um Zeit zu sparen.return (SELF) this;
kompiliert wirdreturn this;
, sodass Sie sie einfach weglassen können.Sie sind nicht der einzige, der sich fragt, was das bedeutet. Siehe Chaotischer Java-Blog .
"Wenn eine Klasse diese Klasse erweitert, sollte sie einen Parameter E übergeben. Die Grenzen des Parameters E gelten für eine Klasse, die diese Klasse mit demselben Parameter E erweitert."
quelle
Dieser Beitrag hat mir dieses Problem der 'rekursiven generischen Typen' vollständig geklärt. Ich wollte nur einen weiteren Fall hinzufügen, in dem diese spezielle Struktur notwendig ist.
Angenommen, Sie haben generische Knoten in einem generischen Diagramm:
Dann können Sie Diagramme von speziellen Typen haben:
quelle
class Foo extends Node<City>
in der Foo nichts mit City zu tun hat.class Node<T>
?class Node<T>
stimmt voll und ganz mit Ihrem Beispiel überein.Wenn Sie sich den
Enum
Quellcode ansehen , hat er Folgendes:Was bedeutet als erstes
E extends Enum<E>
? Dies bedeutet, dass der Typparameter von Enum ausgeht und nicht mit einem Rohtyp parametrisiert wird (er wird von selbst parametrisiert).Dies ist relevant, wenn Sie eine Aufzählung haben
was, wenn ich es richtig weiß, übersetzt wird
Dies bedeutet, dass MyEnum die folgenden Methoden erhält:
Und noch wichtiger:
Dadurch wird
getDeclaringClass()
auf das richtigeClass<T>
Objekt gegossen .Ein klareres Beispiel ist das, das ich auf diese Frage beantwortet habe, bei der Sie dieses Konstrukt nicht vermeiden können, wenn Sie eine generische Grenze angeben möchten.
quelle
compareTo
oder wasgetDeclaringClass
dieextends Enum<E>
Bindung erfordert .Laut Wikipedia wird dieses Muster als seltsam wiederkehrendes Vorlagenmuster bezeichnet . Grundsätzlich können wir durch Verwendung des CRTP-Musters leicht auf Unterklassentypen ohne Typumwandlung verweisen, was bedeutet, dass wir durch Verwendung des Musters die virtuelle Funktion imitieren können.
quelle