Javaの列挙型(Enum)へ新しい要素を追加2 - Javassist

前回Java の列挙型(Enum)へ新しい要素(識別子)を追加するためリフレクションを駆使しましたが、今回は Javassist を使ってもっと容易に実現する方法をご紹介します。

ソースは http://github.com/fits/try_samples/tree/master/blog/20140316/

はじめに

前回と同様の列挙型へ Second 要素を追加してみる事にします。

EType.java
enum EType {
    First
}

Javassist を使った列挙型への要素追加

Javassist を使う場合、下記を実施して $VALUES フィールドへ列挙型の要素を好きなように設定するだけです。

  • CtConstructorinsertAfter メソッドを使って、静的初期化子へ $VALUES を書き換える処理を挿入

なお、静的初期化子のための CtConstructor オブジェクトは CtClass オブジェクトの getClassInitializerメソッドで取得します。

EnumAddValueJavassist.java
import javassist.*;

public class EnumAddValueJavassist {
    public static void main(String... args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass cc = pool.get("EType");

        // 静的初期化子(static イニシャライザ)へ $VALUES を変更する処理を追加
        cc.getClassInitializer().insertAfter("$VALUES = new EType[] { First, new EType(\"Second\", 1) };");

        cc.toClass();

        System.out.println(EType.valueOf("Second"));

        System.out.println("-----");

        for (EType type : EType.values()) {
            System.out.println(type);
        }
    }
}

実行すると下記のように Second の追加を確認できます。

ビルドと実行
> javac -cp .;javassist-3.18.1-GA.jar *.java

> java -cp .;javassist-3.18.1-GA.jar EnumAddValueJavassist
Second
-----
First
Second

実際は、下記のように Second クラスフィールドを追加しておいた方が望ましいかもしれません。

EnumAddValueJavassist2.java
import javassist.*;

public class EnumAddValueJavassist2 {
    public static void main(String... args) throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass cc = pool.get("EType");

        // Second フィールドの追加
        CtField second = CtField.make("public static final EType Second = new EType(\"Second\", 1);", cc);
        cc.addField(second);

        cc.getClassInitializer().insertAfter("$VALUES = new EType[] { First, Second };");

        cc.toClass();
        ・・・
    }
}