Java列挙型は素晴らしいです。ジェネリック医薬品も同様です。もちろん、型消去のために後者の制限を知っています。しかし、理解できないことが1つあります。なぜこのような列挙型を作成できないのですか。
_public enum MyEnum<T> {
LITERAL1<String>,
LITERAL2<Integer>,
LITERAL3<Object>;
}
_
この汎用型パラメーター_<T>
_は、さまざまな場所で役立つ可能性があります。メソッドへのジェネリック型パラメーターを想像してください:
_public <T> T getValue(MyEnum<T> param);
_
または、enumクラス自体でも:
_public T convert(Object o);
_
上記の例は一部の人にとっては抽象的すぎるように思えるかもしれないので、ここに私がこれをしたい理由のより現実的な例を示します。この例では、使用したい
_public interface MyProperties {
public <T> void put(MyEnum<T> key, T value);
public <T> T get(MyEnum<T> key);
}
_
データ型の列挙があります。
_public interface DataType<T> {}
public enum SQLDataType<T> implements DataType<T> {
TINYINT<Byte>,
SMALLINT<Short>,
INT<Integer>,
BIGINT<Long>,
CLOB<String>,
VARCHAR<String>,
...
}
_
各列挙リテラルは、明らかに、ジェネリック型_<T>
_に基づく追加のプロパティを持ち、同時に列挙型(不変、シングルトン、列挙可能など)になります。
誰もこれを考えませんでしたか?これはコンパイラ関連の制限ですか?キーワード "enum"は、JVMに対して生成されたコードを表す構文シュガーとして実装されているという事実を考慮すると、この制限は理解できません。
誰がこれを私に説明できますか?答える前に、これを考慮してください:
String string = LITERAL1.convert(myObject); Integer integer = LITERAL2.convert(myObject);
のようなものを記述するときにどの型を適用するかを知っています。T getvalue()
メソッドのジェネリック型パラメーターにも同じことが当てはまります。コンパイラは、String string = someClass.getValue(LITERAL1)
を呼び出すときに型キャストを適用できますこれは JEP-301 Enhanced Enums の時点で議論されています。 JEPに示されている例は、まさに私が探していたものです。
enum Argument<X> { // declares generic enum
STRING<String>(String.class),
INTEGER<Integer>(Integer.class), ... ;
Class<X> clazz;
Argument(Class<X> clazz) { this.clazz = clazz; }
Class<X> getClazz() { return clazz; }
}
Class<String> cs = Argument.STRING.getClazz(); //uses sharper typing of enum constant
残念ながら、JEPはまだ重大な問題に苦労しています: http://mail.openjdk.Java.net/pipermail/amber-spec-experts/2017-May/000041.html
答えは質問にあります:
型の消去のため
引数の型が消去されるため、これら2つの方法はいずれも使用できません。
public <T> T getValue(MyEnum<T> param);
public T convert(Object);
ただし、これらのメソッドを実現するには、enumを次のように構築できます。
public enum MyEnum {
LITERAL1(String.class),
LITERAL2(Integer.class),
LITERAL3(Object.class);
private Class<?> clazz;
private MyEnum(Class<?> clazz) {
this.clazz = clazz;
}
...
}
できないから。真剣に。これは言語仕様に追加できます。そうではありません。複雑さが増します。費用対効果は、優先度が高くないことを意味します。
更新:現在、言語の JEP 301:Enhanced Enums に追加されています。
ENUMには他にも機能しないメソッドがあります。 MyEnum.values()
は何を返しますか?
MyEnum.valueOf(String name)
はどうですか?
ValueOfについて、コンパイラが次のような一般的なメソッドを作成できると思う場合
public static MyEnum valueOf(String name);
MyEnum<String> myStringEnum = MyEnum.value("some string property")
のように呼び出すためには、それも機能しません。たとえば、MyEnum<Int> myIntEnum = MyEnum.<Int>value("some string property")
を呼び出すとどうなりますか?タイプ消去のため、MyEnum.<Int>value("some double property")
のように呼び出したときに例外をスローしたり、nullを返すなど、正しく動作するようにそのメソッドを実装することはできません。
率直に言って、これは何よりも問題を探すための解決策のようです。
Java enumの全体的な目的は、類似のプロパティを共有する型インスタンスの列挙をモデル化することで、同等の文字列または整数表現を超える一貫性と豊富さを提供することです。
教科書の列挙型の例をご覧ください。これはあまり便利でも一貫性もありません:
public enum Planet<T>{
Earth<Planet>,
Venus<String>,
Mars<Long>
...etc.
}
異なる惑星に異なるジェネリック型変換を持たせたいのはなぜですか?どのような問題を解決しますか?言語のセマンティクスを複雑にすることを正当化しますか?この動作が必要な場合、列挙型はそれを達成するための最良のツールですか?
さらに、複雑なコンバージョンをどのように管理しますか?
例えば
public enum BadIdea<T>{
INSTANCE1<Long>,
INSTANCE2<MyComplexClass>;
}
String
Integer
で簡単に名前または序数を指定できます。ただし、ジェネリックを使用すると、任意の型を指定できます。 MyComplexClass
への変換をどのように管理しますか?ここで、ジェネリック列挙型に提供できる型の限られたサブセットがあることをコンパイラーに強制し、すでに多くのプログラマーを避けているように思われる概念(ジェネリック)に追加の混乱を導入することにより、コンパイラーに2つの構成要素をいじります。