HINWEIS: Diese Frage ist nicht Enum-bezogen, so dass es nicht doppelt ist. Enum sind gezwungen, nur mit sich selbst zu vergleichen, weil Compiler Generierung von Typ Parameter, nicht, weil Java rekursive Typ Parameter / p>
Ich versuche einen Vorteil zu finden, um eine Klasse zu deklarieren als:
%Vor%versus Erklärung als:
%Vor% Ich habe versucht, Methoden bereitzustellen, die E
zurückgeben, und Methoden, die Some<E>
zurückgeben, verschiedene Querverweise in komplizierter Klassenhierarchie und jedes Mal, wenn ich versucht habe, zusätzliche <E>
zu entfernen - keine neuen Fehler / Warnungen kamen hoch .
Können Sie mir eine Methode zeigen, die den Vorteil dieser zusätzlichen <E>
beweist?
Ich nehme an, dass es wegen JDK-Deklarationen einen gibt: <E extends Comparable<? super E>>
Antworten auf andere Fragen zu SO gibt zum Beispiel:
Mit dem zusätzlichen Konstrukt wissen Sie, dass jede Klasse erweitert wird Enum ist nur mit sich selbst vergleichbar.
Aber ich kann diese Theorie leicht brechen:
%Vor%Trotz der generischen Rekursion kann ich immer noch Hund mit Katze vergleichen:
%Vor%Transalting Theorie:
Sie wissen, dass jede Klasse, die Animal ausdehnt, nur mit sich selbst vergleichbar ist
das ist falsch - ich habe einen Klassenhund, der Tier mit Katze ausdehnt, nicht selbst.
Es gibt sehr wenige Situationen, in denen eine Grenze wie class Some<E extends Some<E>>
notwendig ist. Die meiste Zeit schreiben die Leute das, die Grenze wird nicht wirklich im Code verwendet, und class Some<E>
würde genauso gut funktionieren.
Es gibt jedoch bestimmte Situationen, in denen die Grenze in class Some<E extends Some<E>>
tatsächlich verwendet wird. Zum Beispiel:
Was Ihre Frage angeht - was ist mit class Some<E extends Some>
? Nun, das erste Problem ist, dass Sie einen unformatierten Typ verwenden. Rohtypen sollten niemals in neuem Code verwendet werden. Aber Sie sind nicht überzeugt.
Der Rohtyp mit der obigen Klasse ( class Some<E extends Some>
) wird mit einer Warnung kompiliert (die Sie auf Ihre eigene Gefahr ignorieren können). Der rohe Typ bedeutet jedoch, dass damit auch unsichere Dinge möglich sind.
Es bedarf einiger Anstrengungen, um ein Beispiel zu finden, um zu zeigen, dass es unsicher ist. Hier ist eins:
%Vor% Der Code wird mit Warnungen, aber ohne Fehler kompiliert und löst zur Laufzeit ein ClassCastException
aus.
Betreffend die Aussage
jedes Mal, wenn ich versucht habe, zusätzliche zu entfernen - keine neuen Fehler / Warnungen kamen auf.
Dies sollte nicht der Fall sein. Es sollte eine Warnung ausgegeben werden, weil Sie den rohen Typ Some
verwenden, und das Ergebnis ist fehlende Typsicherheit, wie im Antwort von newacct .
Das Dog
/ Cat
Beispiel ist ein bisschen künstlich und etwas fehlerhaft. Sie haben vorgeschlagen, eine Klasse zu deklarieren
Aber hier bedeutet der Typparameter im Grunde: "Dieser Parameter ( Cat
) ist der Typ, mit dem Objekte dieser Klasse ( Dog
) mit" verglichen werden können. Sie sagen also ausdrücklich , dass ein Dog
mit Cat
vergleichbar sein sollte. Selbst mit anspruchsvollen Sprachen und intelligenten Compilern liegt es in der Verantwortung der Programmierer, Code zu schreiben, der Sinn macht.
Tatsächlich gibt es nicht viele Fälle, in denen diese selbstreferentiellen generischen Typen notwendig sind. Eines dieser Beispiele ist in diesem FAQ-Eintrag skizziert: Er deklariert eine Struktur von Knoten (d. H , ein Baum), wobei der type-Parameter verwendet werden kann, um die Definition der Struktur Struktur vom tatsächlichen Typ der Knoten zu entkoppeln:
%Vor%Aber aus meiner persönlichen Erfahrung kann ich sagen, dass wenn Sie denken, dass Sie einen solchen Typ erstellen müssen, sollten Sie gründlich über die Vor- und Nachteile nachdenken. Letzteres bezieht sich hauptsächlich auf eine verringerte Lesbarkeit. Wenn Sie die Wahl zwischen Methoden wie
haben %Vor%das sind typsicher, oder Methoden wie
%Vor%die nicht typsicher sind (wegen roher Typen oder einfach weil die Typen nicht generisch sind), dann würde ich letztere bevorzugen. Code wird von Menschen gelesen.
Der Unterschied ist, dass Some<E extends Some>
zwei Dinge bedeutet:
E
ist ein Rohtyp. Kurz gesagt, bei Rohtypen werden alle generischen Informationen aus der Klasse entfernt, was wiederum zu unerwartetem Verhalten führen kann E
kann jede Klasse von Some
! Umgekehrt bedeutet die Verwendung von Some<E extends Some<E>>
:
E
wird eingegeben E
muss die selbe -Klasse der Klasse sein, in der sie deklariert ist Der rohe Teil ein Problem, aber die größere Implikation sind die Typgrenzen. Dieser Code demonstriert den Unterschied, wobei die "B" -Klassen die Klassen "A" als generischen Typ verwenden (oder versuchen, sie zu verwenden).
%Vor% Klasse SomeB
kompiliert gut - der Typ von E ist nur an Some
gebunden, also any Some
Klasse, aber die Klasse SomeTB
löst einen Kompilierungsfehler aus:
Typ Argument SomeTA ist nicht innerhalb der Grenzen der Typ-Variable E
Der Typ von E
muss genau mit der enthaltenen Klasse übereinstimmen.
Dies ist wichtig, wenn Sie erwarten, dass Parameter und Rückgabetypen mit der Klasse identisch sind, was normalerweise bei typisierten Klassen der Fall ist.
Der Typ, der von einem unformatierten Typ begrenzt wird, ist weniger ein Problem, weil es immer noch ein Some
ist, aber es kann Fälle geben, in denen es einen Unterschied macht (ich kann mir gerade keinen vorstellen).
Es sieht aus wie zur Zeit hat Some<E extends Some<E>>
keinen Vorteil gegenüber Some<E extends Some>
Laut Artikel in Zusammenarbeit mit Java Champions und dem Ersteller von Java Generics FAQ :
Artikel gibt einige Möglichkeiten / Annahmen:
Ich denke, Enum wäre ausreichend gewesen. Allerdings, wie Philip Wadler mich darauf hingewiesen hat, da Enum ein Generikum ist Klasse, sollten wir es immer nur in Verbindung mit einem Typ verwenden. Im Zukünftig könnte Java strenger und rohe Typen werden illegal. Wir haben daher die Wahl, entweder
Enum<E extends Enum<E>>
zu schreiben oderEnum<E extends Enum<?>>
, von denen die erste Option mehr ist genau. Auch wenn der Compiler mir momentan keine a zeigt Unterschied, könnte ich in Zukunft Warnungen sehen, also folge ich diesem Idiom meine Klasse auch
Tags und Links java generics enums recursion type-parameter