java 修改 枚举类字段_枚举枚举和修改“最终静态”字段的方法

java 修改 枚举类字段

在本新闻通讯中,该新闻通讯最初发表在Java专家的新闻通讯第161期中,我们研究了如何使用sun.reflect包中的反射类在Sun JDK中创建枚举实例。 显然,这仅适用于Sun的JDK。 如果需要在另一个JVM上执行此操作,则您可以自己完成。

所有这些都始于爱丁堡的Ken Dobson的电子邮件,该电子邮件向我指出了sun.reflect.ConstructorAccessor的方向,他声称可以将其用于构造枚举实例。 我以前的方法(通讯#141)在Java 6中不起作用。

我很好奇为什么Ken要构造枚举。 这是他想使用它的方式:

public enum HumanState {
  HAPPY, SAD
}

public class Human {
  public void sing(HumanState state) {
    switch (state) {
      case HAPPY:
        singHappySong();
        break;
      case SAD:
        singDirge();
        break;
      default:
         new IllegalStateException("Invalid State: " + state);
    }
  }

  private void singHappySong() {
    System.out.println("When you're happy and you know it ...");
  }

  private void singDirge() {
    System.out.println("Don't cry for me Argentina, ...");
  }
}

上面的代码需要进行单元测试。 你发现错误了吗? 如果没有,请使用细梳再次遍历代码以尝试找到它。 当我第一次看到这个时,我也没有发现错误。

当我们产生这样的错误时,我们应该做的第一件事就是进行一个显示它的单元测试。 但是,在这种情况下,我们无法使default情况发生,因为HumanState仅具有HAPPY和SAD枚举。

Ken的发现使我们可以使用sun.reflect包中的ConstructorAccessor类来创建枚举的实例。 它涉及到以下内容:

Constructor cstr = clazz.getDeclaredConstructor(
  String.class, int.class
);
ReflectionFactory reflection =
  ReflectionFactory.getReflectionFactory();
Enum e =
  reflection.newConstructorAccessor(cstr).newInstance("BLA",3);

但是,如果仅执行此操作,则最终会出现ArrayIndexOutOfBoundsException,这在我们看到Java编译器如何将switch语句转换为字节代码时才有意义。 以上面的Human类为例,下面是反编译后的代码(感谢Pavel Kouznetsov的JAD ):

public class Human {
  public void sing(HumanState state) {
    static class _cls1 {
      static final int $SwitchMap$HumanState[] =
        new int[HumanState.values().length];
      static {
        try {
          $SwitchMap$HumanState[HumanState.HAPPY.ordinal()] = 1;
        } catch(NoSuchFieldError ex) { }
        try {
          $SwitchMap$HumanState[HumanState.SAD.ordinal()] = 2;
        } catch(NoSuchFieldError ex) { }
      }
    }

    switch(_cls1.$SwitchMap$HumanState[state.ordinal()]) {
      case 1:
        singHappySong();
        break;
      case 2:
        singDirge();
        break;
      default:
        new IllegalStateException("Invalid State: " + state);
        break;
    }
  }
  private void singHappySong() {
    System.out.println("When you're happy and you know it ...");
  }
  private void singDirge() {
    System.out.println("Don't cry for me Argentina, ...");
  }
}

您可以立即看到为什么要得到ArrayIndexOutOfBoundsException,这要归功于内部类_cls1。

我第一次尝试解决此问题并没有得到一个不错的解决方案。 我试图在HumanState枚举中修改$ VALUES数组。 但是,我只是摆脱了Java的保护性代码。 您可以修改final字段 ,只要它们是非静态的即可。 这种限制对我来说似乎是人为的,因此我着手寻找静态最终领域的圣杯。 再次,它被隐藏在阳光反射的房间里。

设置“最终静态”字段

设置final static字段需要几件事。 首先,我们需要使用法线反射获取Field对象。 如果将其传递给FieldAccessor,我们将退出安全代码,因为我们正在处理静态的final字段。 其次,我们将Field对象实例内的修饰符字段值更改为非最终值。 第三,我们将经过修改的字段传递给sun.reflect包中的FieldAccessor并使用它进行设置。

这是我的ReflectionHelper类,可用于通过反射设置final static字段:

import sun.reflect.*;
import java.lang.reflect.*;

public class ReflectionHelper {
  private static final String MODIFIERS_FIELD = "modifiers";

  private static final ReflectionFactory reflection =
      ReflectionFactory.getReflectionFactory();

  public static void setStaticFinalField(
      Field field, Object value)
      throws NoSuchFieldException, IllegalAccessException {
    // we mark the field to be public
    field.setAccessible(true);
    // next we change the modifier in the Field instance to
    // not be final anymore, thus tricking reflection into
    // le
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值