Java-Enums sind großartig. Generika auch. Natürlich kennen wir alle die Einschränkungen des letzteren aufgrund der Typlöschung. Aber eines verstehe ich nicht: Warum kann ich so eine Aufzählung nicht erstellen:
public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
Dieser generische Typparameter <T>
könnte dann wiederum an verschiedenen Stellen nützlich sein. Stellen Sie sich einen generischen Typparameter für eine Methode vor:
public <T> T getValue(MyEnum<T> param);
Oder sogar in der Enum-Klasse selbst:
public T convert(Object o);
Konkreteres Beispiel Nr. 1
Da das obige Beispiel für manche zu abstrakt erscheint, ist hier ein realistischeres Beispiel dafür, warum ich dies tun möchte. In diesem Beispiel möchte ich verwenden
- Aufzählungen, weil ich dann einen endlichen Satz von Eigenschaftsschlüsseln aufzählen kann
- Generika, weil ich dann Typensicherheit auf Methodenebene zum Speichern von Eigenschaften haben kann
public interface MyProperties {
public <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
Konkreteres Beispiel Nr. 2
Ich habe eine Aufzählung von Datentypen:
public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
Jedes Aufzählungsliteral hätte offensichtlich zusätzliche Eigenschaften, die auf dem generischen Typ basieren <T>
, während es gleichzeitig eine Aufzählung ist (unveränderlich, singleton, aufzählbar usw. usw.).
Frage:
Hat niemand daran gedacht? Ist dies eine compilerbezogene Einschränkung? In Anbetracht der Tatsache, dass das Schlüsselwort " enum " als syntaktischer Zucker implementiert ist und generierten Code für die JVM darstellt, verstehe ich diese Einschränkung nicht.
Wer kann mir das erklären? Beachten Sie Folgendes, bevor Sie antworten:
- Ich weiß, dass generische Typen gelöscht werden :-)
- Ich weiß, dass es Problemumgehungen mit Klassenobjekten gibt. Sie sind Problemumgehungen.
- Generische Typen führen gegebenenfalls zu vom Compiler generierten Typumwandlungen (z. B. beim Aufrufen der convert () -Methode
- Der generische Typ <T> würde in der Aufzählung stehen. Daher ist es an jedes Literal der Aufzählung gebunden. Daher würde der Compiler wissen, welchen Typ er beim Schreiben von so etwas anwenden soll
String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
- Gleiches gilt für den generischen Typparameter in der
T getvalue()
Methode. Der Compiler kann beim Aufruf Type Casting anwendenString string = someClass.getValue(LITERAL1)
enum
Folgendes : Verwandeln Sie das in eine "typsichere Aufzählung", die wir vor Java 1.5 verwendet haben. Plötzlich können Sie Ihre Enum-Mitglieder parametrisieren lassen. Das werde ich jetzt wahrscheinlich tun.Antworten:
Dies wird jetzt ab JEP-301 Enhanced Enums diskutiert . Das im JEP gegebene Beispiel ist genau das, wonach ich gesucht habe:
Leider hat das JEP immer noch mit erheblichen Problemen zu kämpfen: http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html
quelle
Die Antwort liegt in der Frage:
Keine dieser beiden Methoden ist möglich, da der Argumenttyp gelöscht wird.
Um diese Methoden zu realisieren, können Sie Ihre Aufzählung jedoch wie folgt erstellen:
quelle
public T convert(Object);
. Ich vermute, diese Methode könnte zum Beispiel eine Reihe verschiedener Typen auf <T> eingrenzen, was zum Beispiel ein String ist. Die Konstruktion des String-Objekts ist Laufzeit - nichts, was der Compiler tut. Daher müssen Sie den Laufzeittyp kennen, z. B. String.class. Oder fehlt mir etwas?public final static String FOO;
mit einem statischen Blockstatic { FOO = "bar"; }
- auch Konstanten werden ausgewertet, selbst wenn sie zur Kompilierungszeit bekannt sind.Weil du nicht kannst. Ernsthaft. Das könnte der Sprachspezifikation hinzugefügt werden. Das war es nicht. Dies würde die Komplexität erhöhen. Dieser Kostenvorteil bedeutet, dass es keine hohe Priorität hat.
Update: Wird derzeit zur Sprache unter JEP 301: Enhanced Enums hinzugefügt .
quelle
Es gibt andere Methoden in ENUM, die nicht funktionieren würden. Was würde
MyEnum.values()
zurückkehren?Was ist mit
MyEnum.valueOf(String name)
?Für den Wert von Wenn Sie denken, dass der Compiler generische Methoden wie machen könnte
um es so zu nennen
MyEnum<String> myStringEnum = MyEnum.value("some string property")
, würde das auch nicht funktionieren. Was ist zum Beispiel, wenn Sie anrufenMyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
? Es ist nicht möglich, diese Methode so zu implementieren, dass sie ordnungsgemäß funktioniert, z. B. um eine Ausnahme auszulösen oder null zurückzugeben, wenn Sie sieMyEnum.<Int>value("some double property")
aufgrund des Löschens des Typs wie folgt aufrufen .quelle
MyEnum<?>[] values()
undMyEnum<?> valueOf(...)
MyEnum<Int> blabla = valueOf("some double property");
nicht vornehmen, da die Typen nicht kompatibel sind. Außerdem möchten Sie in diesem Fall null erhalten, weil Sie MyEnum <Int> zurückgeben möchten, das für den Namen der doppelten Eigenschaft nicht vorhanden ist, und diese Methode aufgrund des Löschvorgangs nicht ordnungsgemäß funktionieren kann.Enum
es viele andere nützliche Funktionen gibt, und diese wäre optional und auch sehr nützlich ...Ehrlich gesagt scheint dies eher eine Lösung auf der Suche nach einem Problem zu sein als alles andere.
Der gesamte Zweck der Java-Aufzählung besteht darin, eine Aufzählung von Typinstanzen zu modellieren, die ähnliche Eigenschaften aufweisen, und zwar auf eine Weise, die Konsistenz und Reichhaltigkeit bietet, die über die vergleichbarer String- oder Integer-Darstellungen hinausgeht.
Nehmen Sie ein Beispiel für eine Lehrbuchaufzählung. Dies ist nicht sehr nützlich oder konsistent:
Warum sollte ich wollen, dass meine verschiedenen Planeten unterschiedliche generische Typkonvertierungen haben? Welches Problem löst es? Rechtfertigt es, die Sprachsemantik zu komplizieren? Wenn ich dieses Verhalten brauche, ist eine Aufzählung das beste Werkzeug, um es zu erreichen?
Wie würden Sie außerdem komplexe Conversions verwalten?
zum Beispiel
Es ist einfach genug
String
Integer
, den Namen oder die Ordnungszahl anzugeben. Mit Generika können Sie jedoch jeden Typ liefern. Wie würden Sie die Konvertierung verwaltenMyComplexClass
? Jetzt machen Sie zwei Konstrukte kaputt, indem Sie den Compiler dazu zwingen, zu wissen, dass es eine begrenzte Teilmenge von Typen gibt, die generischen Enums zur Verfügung gestellt werden können, und zusätzliche Verwirrung in das Konzept (Generics) bringen, die sich bereits vielen Programmierern zu entziehen scheint.quelle
interface Converter<IN, OUT> { OUT convert(IN in); } <E> Set<E> convertListToSet(List<E> in, Converter<? super List<E>, ? extends Set<E>> converter) { return converter.convert(in); }
Wir müssen jedes Mal manuell herausfinden, welcher Typ verbraucht und welcher produziert wird, und die Grenzen entsprechend angeben.Weil "Aufzählung" die Abkürzung für Aufzählung ist. Es ist nur eine Reihe benannter Konstanten, die anstelle von Ordnungszahlen stehen, um den Code besser lesbar zu machen.
Ich sehe nicht, was die beabsichtigte Bedeutung einer typparametrisierten Konstante sein könnte.
quelle
java.lang.String
:public static final Comparator<String> CASE_INSENSITIVE_ORDER
. Du siehst jetzt? :-)CASE_INSENSITIVE_ORDER
... WennComparator<T>
es sich um eine Aufzählung handelt, warum nicht Literale mit einer zugehörigen Bindung<T>
?Ich denke, weil Enums im Grunde nicht instanziiert werden können
Wo würden Sie die T-Klasse einstellen, wenn JVM dies zulässt?
Aufzählung sind Daten, die immer gleich sein sollen oder sich zumindest nicht dinamisch ändern.
neues MyEnum <> ()?
Dennoch kann der folgende Ansatz nützlich sein
quelle
setO()
Methode auf einem gefälltenum
. Ich betrachte Aufzählungskonstanten und für mich bedeutet das unveränderlich . Selbst wenn es möglich wäre, würde ich es nicht tun.