java - 包含 2000+1 个枚举常量的枚举类是否达到任何限制?

标签 java enums

以下代码在主 (map==null) 中因 NullPointerException 而失败。 仅当我定义 2001 个或更多枚举常量时才会出现此问题,2000 个工作正常。

为什么静态代码块不执行?

我们是否达到编译器(无警告、无错误)或 JVM 的静默限制?

编译后的类文件超过172KB,

import java.util.HashMap;

public enum EnumTest {
    E(1),E(2),...,E(2001);

    private static HashMap<Integer, EnumTest>   map = new HashMap<Integer, EnumTest>();

    static {

        for ( EnumTest f : EnumTest.values() ) {
            map.put( (int) f.id, f );
        }
    }
    short id;

    private EnumTest(int id) {
        this.id = (short) id;
    };

    public short getId() {
        return id;
    }

    public static final EnumTest fromInt(int id) {
        EnumTest e = map.get( id );
        if ( e != null ) {
            return e;
        }
        throw new IllegalArgumentException( "" + id );
    }

    public static void main(String[] args) {
        System.out.println( "size:" + map.size() );
    }
}

Runtime Environment:

java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)

也发生在:

java version "1.6.0_32" Java(TM) SE Runtime Environment (build
1.6.0_32-b05) Java HotSpot(TM) Client VM (build 20.7-b02, mixed mode, sharing)

最佳答案

这类问题源于某些(通常是编译器生成的)初始化代码超过 65536 字节的字节码。单个方法不能包含超过要执行的字节码字节数(由于 restrictions in the class file format )。

此类问题的常见来源是像这样的大型数组:

byte someBytes = { 1, 2, 3, ..., someBigValue };

这里的问题是这些字段实际上是在生成的初始化程序(构造函数或静态初始化程序)中使用 someBigValue 赋值语句初始化的。

枚举值实际上以类似的方式初始化。

给定以下枚举类:

public enum Foo {
  CONSTANT(1);

  private Foo(int i) {
  }
}

我们查看javap -v的输出,看到如下代码块:

  static {};
    flags: ACC_STATIC
    Code:
      stack=5, locals=0, args_size=0
         0: new           #4                  // class Foo
         3: dup
         4: ldc           #7                  // String CONSTANT
         6: iconst_0
         7: iconst_1
         8: invokespecial #8                  // Method "<init>":(Ljava/lang/String;II)V
        11: putstatic     #9                  // Field CONSTANT:LFoo;
        14: iconst_1
        15: anewarray     #4                  // class Foo
        18: dup
        19: iconst_0
        20: getstatic     #9                  // Field CONSTANT:LFoo;
        23: aastore
        24: putstatic     #1                  // Field $VALUES:[LFoo;
        27: return

如您所见,有相当多的字节码操作可以使用正确的值处理实例化 CONSTANT。如果您有许多这样的枚举值,那么该静态初始化程序 block 的大小很容易超过 64k 字节的代码,从而使该类无法编译。

一个可能的解决方法是通过减少参数的数量来减少初始化代码的大小(例如,根据枚举值的索引而不是使用参数来计算传入的数字)。这可能只是给你足够的回旋余地来进一步扩展它。

或者,您可以尝试将您的枚举拆分为多个通过实现通用接口(interface)连接的枚举。枚举可以按区域/意图/类别/...分组:

public interface MessageType {
  int getId();
}

public enum ConnectionMessage implements MessageType {
  INIT_CONNECTION(1),
  LOGIN(2),
  LOGOUT(3),
  CLOSE_CONNECTION(4);

  // getId code, constructor, ...
}

public enum FrobnicationMessage implements MessageType {
  FROBNICATE_FOO(5),
  FROBNICATE_BAR(6),
  DEFROB_FOO(7),
  DEFROB_BAR(8),
  ...

  // getId code, constructor, ...
}

我假设枚举值实际上在您的代码中的某处被引用而不仅仅是纯值持有者,如果它们仅持有值并且在您的代码中没有区别对待各个值,那么将它们替换为实例化一次的单个类每个数据项都存储在中央资源中可能是最好的方法。

关于java - 包含 2000+1 个枚举常量的枚举类是否达到任何限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11883043/

相关文章:

java - java中获取像素颜色

c - 如何初始化枚举?

java - 将业务逻辑与基于枚举的单例类分开

Java - 从字符串中获取整数

c# - 检查字节是否是枚举中的值

javascript - 将枚举从 Typescript 转换为 JavaScript 时出错

java - 如何将枚举与命令行参数一起使用?

java - 从 JSF 中的 EJB 处理验证错误

java - 当我尝试子类化 ServiceConnection 时得到 "no interface expected here"

java - 进程运行时在窗口中显示计时器