Javaの列挙型がジェネリクスをサポートし、個々の項目にメソッドを追加する機能が追加された。これは新たなJEPで公開されている。どちらの機能も、一緒にバンドルされているため、1回のアップデートで提供される。この変更はJavaコンパイラのみに影響を与えるため、ランタイムの変更は必要ない。ターゲットとするバージョンはないが、おそらくJava 10になるであろう。
この変更については当初、Joshua Bloch氏のような有名なJava Championsがその有用性に疑問を呈しており、よい印象ではなかった。しかし、新しいユースケースの議論やプレゼンテーションを重ねることで、支持を得られるようになった。
@BrianGoetzへの返信を見ていないかもしれないので、私はJEPの要点を鑑みて、異議を撤回します。ユースケース:https://t.co/O1tJO8oSCp
— Joshua Bloch (@joshbloch) 2016年12月7日
JEPや他の議論で提示されたユースケースを要約することで、開発者がどのように変更できるのかを見ていこう。Java ChampionのLukas Eder氏はStackOverflowの質問を挙げた。そこでは、設定ファイル、Webセッション、あるいはそれに類似したものから、タイプセーフにプロパティを参照、設定できるようになることが挙げられている。ジェネリクスをサポートする列挙型では、使用可能なキーのセットとそれに紐付く型を指定できる。
public enum Key<T> {
HOST<String>,
PORT<Integer>,
SCORE<Double>
}
public interface PropertiesStore {
public <T> void put(Key<T> key, T value);
public <T> T get(Key<T> key);
}
これらのキーは、次のような式ではコンパイルに失敗するため、キーをプロパティストアから安全に取得、あるいは格納できる。
put(PORT, “not a number”); // error, type mismatch: PORT is Key<Integer>
// “not a number” is String
一方、個々の項目に独自のメソッドを持たせられるようになることで、特定のプロパティのみに適用できる操作を定義できるようになる。JEP 301では、上記の定義を次のように拡張することができる。
public enum Key<T> {
HOST<String>,
PORT<Integer>,
SCORE<Double> {
double normalise(double x) {
// score normalisation logic
return result;
}
}
}
現在の列挙型を使用すると、すべての項目は、一般的な型Key
を持つことを意味し、normalise
メソッドは見えない。しかし、列挙型の変更が完了すると、コンパイラはこの種の情報を保持する。つまり、次の通りに動作する。
SCORE.normalise(5.37); // compiles
HOST.normalise(5.37); // error: neither HOST nor Key have normalise
列挙型の変更に対応する方法は、列挙型の個々の項目の公開された静的型がどのように計算されるかを変更することである。読者は知っているであろうが、列挙型はJava 5で単なる糖衣構文として追加されたものである。JVMは列挙型に対して特別な処理はしておらず、代わりに、コンパイラが列挙型を静的オブジェクトを持つ通常のクラスに変換し、その後、バイトコードにコンパイルしている。技術的な側面はさておき、以下の列挙型:
public enum Colour {
RED, GREEN, BLUE
}
は、大まかに言うと、コンパイラによって次のように翻訳される(これは正確な表現ではないが、ここでの説明には十分である)。
public class Colour extends Enum {
public static final Colour RED = new Colour();
public static final Colour GREEN = new Colour();
public static final Colour BLUE = new Colour();
private Colour() {}
}
すべての項目がColour
であるため、項目固有のメソッドや型情報は失われる。JEP 301がすることは、個々の項目を表現するのに、一般的な列挙型を使うことが十分でないケースを特定し、その場合には、Colour$RED
、Colour$GREEN
、Colour$BLUE
のような、より詳細な型を生成することである。
拡張された列挙型は、JDKの他の部分で実行される処理でさらなるメリットがある。一側面として、ローカル変数の型推論により、開発者は、コードを書く時には正確な形式が不明であっても、コンパイラが生成する明確な型を得ることが可能になるかもしれない。つまり、上記のコードを考えると、以下のことが可能になる。
var s = Key.SCORE; // type of s derived as Key$SCORE
s.normalise(8.29); // method normalise can be accessed
もう一方で、事例証拠とサンプルユースケースが示す通り、列挙型でのジェネリクスがプリミティブ型と共に広く使用されると、むしろ非効率的である(JVMはプリミティブ型に対応するオブジェクトクラスを使わなければならない)。これはGenerics over Primitive Typesを扱うProject Valhallaで改善されるはずである。改善により効率性に関する障壁を取り除き、多くの人が利用できるようになるであろう。
Rate this Article
- Editor Review
- Chief Editor Action