Java枚举和其他类文件

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (19)

我已经注意到enums在编译膨胀总大小之后引入许多额外的类文件(Class $ 1)。它似乎附属于甚至使用枚举的每个类,而这些类经常被复制。

为什么会发生这种情况,并且有没有办法在不移除枚举的情况下防止这种情况发生

(问题的原因是空间对我来说很重要)

在进一步调查问题时,Sun的Javac 1.6每次在Enum上使用开关时都会创建一个附加的合成类。它使用某种SwitchMap。这个网站有更多的信息,这里告诉你如何分析Javac正在做什么。

每次在枚举上使用开关时,额外的物理文件似乎都会付出高昂的代价!

有趣的是,Eclipe的编译器不会生成这些附加文件。我想知道唯一的解决方案是切换编译器吗?

提问于
用户回答回答于

我只是被这种行为所左右,当谷歌搜索时出现了这个问题。我想我会分享我发现的一点点额外信息。

每次在枚举上使用开关时,javac 1.5和1.6都会创建一个附加的合成类。该类包含一个所谓的“切换映射”,它将枚举索引映射到切换表跳转数字。重要的是,合成类是为交换发生的类而不是枚举类创建的。

以下是生成内容的示例:

EnumClass.java

public enum EnumClass { VALUE1, VALUE2, VALUE3 }

EnumUser.java

public class EnumUser {
    public String getName(EnumClass value) {
        switch (value) {
            case VALUE1: return "value 1";
            // No VALUE2 case.
            case VALUE3: return "value 3";
            default:     return "other";
        }
    }
}

合成EnumUser $ 1.class

class EnumUser$1 {
    static final int[] $SwitchMap$EnumClass = new int[EnumClass.values().length];

    static {
        $SwitchMap$EnumClass[EnumClass.VALUE1.ordinal()] = 1;
        $SwitchMap$EnumClass[EnumClass.VALUE3.ordinal()] = 2;
    };
}

此切换映射然后用于为一个lookupswitchtableswitchJVM指令生成索引。它将每个枚举值转换为一个对应的索引,从1到[switch cases的数量]。

EnumUser.class

public java.lang.String getName(EnumClass);
  Code:
   0:   getstatic       #2; //Field EnumUser$1.$SwitchMap$EnumClass:[I
   3:   aload_1
   4:   invokevirtual   #3; //Method EnumClass.ordinal:()I
   7:   iaload
   8:   lookupswitch{ //2
                1: 36;
                2: 39;
                default: 42 }
   36:  ldc     #4; //String value 1
   38:  areturn
   39:  ldc     #5; //String value 3
   41:  areturn
   42:  ldc     #6; //String other
   44:  areturn

tableswitch如果有三个或更多开关情况,则会使用它,因为它执行更有效的恒定时间查找与lookupswitch线性搜索。从技术上讲,javac可以在使用合成开关图时省略整个业务lookupswitch

猜测:我手头没有Eclipse的编译器进行测试,但我想像它不会受到合成类的困扰,而只是简单地使用它lookupswitch。或者,它可能需要比原先的提问者在“ugprades”之前测试的更多的切换案例tableswitch

用户回答回答于

当您使用Java枚举的“每实例方法实现”功能时,会出现$ 1等文件,如下所示:

public enum Foo{
    YEA{
        public void foo(){ return true };
    },
    NAY{
        public void foo(){ return false };
    };

    public abstract boolean foo();
}

上面将创建三个类文件,一个用于基本枚举类,另一个用于YEA和NAY来持有foo()的不同实现。

在字节码级别上,枚举只是类,为了让每个枚举实例不同地实现一个方法,每个实例都需要不同的类,

但是,这并没有考虑为枚举用户生成的额外类文件,我怀疑这些只是匿名类的结果,并且与枚举无关。

因此,为了避免生成这些额外的类文件,请不要使用每个实例的方法实现。在上述情况下,如果方法返回常量,则可以使用在构造函数中设置的公共final字段(如果您愿意,也可以使用具有公共getter的私有字段)。如果你真的需要不同逻辑的方法来处理不同的枚举实例,那么你不能避免额外的类,但我认为它是一个相当奇特而且很少需要的特性。

扫码关注云+社区