Enum の疑似拡張
Java の Enum 型は拡張することはできません。 たとえ可能だとしても拡張するべきではありません。 ただし、拡張可能な Enum を利用したくなる場合があります。
オペコードと呼ばれるマシンに対する操作を列挙型で表現したいとします。 そのような場合には、API ユーザーが固有の操作を実装できるようにすることは望ましいことです。
このような場合には、Enum がインタフェースを実装できるという点を利用して、擬似的に Enum を拡張します。 これを Enum の疑似拡張と呼びます。
以下では Operation というインタフェースを宣言し、その標準実装を BasicOperation という Enum として定義しています。
interface Operation { double apply(double x, double y); } public enum BasicOperation implements Operation { PLUS("+") { @Override public double apply(double x, double y) { return x + y; } }, MINUS("-") { @Override public double apply(double x, double y) { return x - y; } }; private final String symbol; BasicOperation(String symbol) { this.symbol = symbol; } }
BasicOperation は Enum であるため拡張できませんが、インタフェース型は拡張可能です。 Operaiton インタフェースを利用し、 カスタマイズされた Operation を新たに定義することができます。
public enum ExtendedOperation implements Operation { EXP("^") { public double apply(double x, double y) { return Math.pow(x, y); } }, REMAINDER("%") { public double apply(double x, double y) { return x % y; } }; private final String symbol; ExtendedOperation(String symbol) { this.symbol = symbol; } }
疑似拡張は実際の拡張とは異なるため、BasicOperation の動作を継承することはできません。 また、拡張できないので継承によるコードの重複を避ける方法は使えません。
もし、コードの重複を避けたい場合には static のヘルパーメソッドなどを作ってコードの重複を除去しましょう。
疑似拡張 Enum の定数の列挙
疑似拡張 Enum を利用する場合、メソッドの引数にはインタフェースで宣言したほうが望ましいです。
Enum をメソッド引数の宣言としていれば、すべての定数を列挙する方法は簡単です。 単に Enum.values() を用いればよいです。
引数として実装したインタフェースをうけとるようにすると、すべての定数を列挙する方法は少し難しくなりますが、不可能ではありません。 Class オブジェクトの getEnumConstants() を使う方法です。
http://docs.oracle.com/javase/jp/7/api/java/lang/Class.html#getEnumConstants()
このメソッドは Class オブジェクトが Enum 型を表していた場合に、すべての定数の配列を返します。 もし、Enum 型を表さない場合には null が返ります。
このメソッドを使ってすべての定数を列挙するコードは以下の様になります。
private static <T extends Enum<T> & Operation> void getAllConstants( Class<T> opSet) { for (Operation op : opSet.getEnumConstants()) { System.out.printf("Operation: %s%n", op); } }
このメソッドは引数として Class<T> を受け取っていますが、その T には以下のような制限がかけられています。
この制約によって、Operation を実装し、Enum であることが保障されるので getEnumConstants() が null を返すことはありません。
感想
このようなテクニックは面白いのだけど、実際使う機会あるかなーという感じがする。 ライブラリや SDK を提供するような場合に使うかもしれない。
次回からはアノテーション。