概要
Javaでは通常外部からアクセスすることのできないprivateフィールドの値を変更する手段として、リフレクションを利用してsetAccessible(true)する方法が広く知られています。
この方法を応用してfinalなフィールドを動的に変更する方法をご紹介します。
サンプルコード
Main.java
public class Main {
public static void main(String[] args) throws Exception {
TargetClass clazz = new TargetClass();
System.out.println("更新前: " + clazz.getTargetField()); // 更新前: 1
Field targetField = clazz.getClass().getDeclaredField("TARGET_FIELD"); // 更新対象アクセス用のFieldオブジェクトを取得する。
Field modifiersField = Field.class.getDeclaredField("modifiers"); // Fieldクラスはmodifiersでアクセス対象のフィールドのアクセス判定を行っているのでこれを更新する。
modifiersField.setAccessible(true); // modifiers自体もprivateなのでアクセス可能にする。
modifiersField.setInt(targetField,
targetField.getModifiers() & ~Modifier.PRIVATE & ~Modifier.FINAL); // 更新対象アクセス用のFieldオブジェクトのmodifiersからprivateとfinalを外す。
targetField.set(null, 1000); // 値更新: 1 -> 1000
System.out.println(" _人人人人人_"); // _人人人人人_
System.out.println("更新後: > " + clazz.getTargetField() + " <"); // 更新後: > 1000 <
System.out.println("  ̄Y^Y^Y^Y ̄"); //  ̄Y^Y^Y^Y ̄
}
}
TargetClass.java
public class TargetClass {
private static final Integer TARGET_FIELD = 1;
public Integer getTargetField() {
return TARGET_FIELD;
}
}
補足
SecurityManagerが有効になっている環境では上記の方法を使用してもprivate static finalなフィールドを動的に変更することはできません。
また、コンパイル時の最適化によってインライン化している場合は、上記の方法でも値を変更することはできません。(この場合はエラーにもなりません。)
参考