Java如何支持Java枚举常量的实例变量?

3
我知道在Java中,enum常量隐式地是一个static final变量。但是enum类可以有一个实例变量,比如说size。所以每个enum常量都会有一个'size'的副本。 这种情况下的等效Java代码是什么?我是说,似乎静态的enum常量正在使用一个非静态的实例变量,这在正常情况下是不可能的?
enum Members{
    A(1),B(2),C(3);  // I have 3 enum constants here

    private int size;
    Members (int size) {
        //System.out.println("Initializing var with size = "+size);
    }
}

到目前为止,我知道的等价代码如下:

public final class Member extends Enums<Members> {
    public static final Members A;
    // ...
    // What happened to size? How do A,B,C get a copy of size?
}

编辑:重新说明我的问题- 我对编译器背后的实现很感兴趣。我已经知道如何使用枚举。我想知道编译器会做什么?例如,如果我只是写一个A,编译器将其转换为"public static final Member A"。我想知道编译器是如何给每个A、B、C都分配大小的拷贝。

我对编译器的后台实现很感兴趣。我已经知道如何使用枚举,但我想知道编译器的处理方式。例如,如果我只是写一个A,编译器会将其转换为"public static final Member A"。我想知道编译器是如何给每个A、B、C分配大小的拷贝。

不要混淆变量和引用与对象。 - Sotirios Delimanolis
2个回答

3
我觉得“静态枚举常量似乎是使用一个非静态的实例变量,这在通常情况下是不可能的?” 是可以的: 在每个枚举中,每个静态变量都是一个独立的对象,具有其自己的实例变量。实例在枚举中是静态的,但它并不会使实例本身的上下文成为静态上下文。
public final class Member extends java.lang.Enums<Members> {
    public static final Members A = new Member(1) {
        public String toString() { return "A:"+size; }
    };
    public static final Members B = new Member(2) {
        public String toString() { return "B:"+size; }
    };
    public static final Members C = new Member(3) {
        public String toString() { return "C:"+size; }
    };
    private final int size;
    protected Member(int size) { this.size = size; }
}

1
为什么您要为每个“Member”变量创建一个新类? - msrd0
1
@rents “这意味着编译器正在进行一些幕后操作。我一直在寻找那个幕后操作。” 绝对的,编译器在你背后做了很多事情,让你使用方便的语法。在泛型、嵌套类、自动装箱和枚举中,发生的事情比你写的要多得多。 - Sergey Kalinichenko
1
@rents "在非枚举类中,静态变量无法访问非静态变量。" 你可能是想说 "静态方法无法访问非静态变量",对吧?这就是诀窍:变量 A、B 和 C 是静态的,但每个变量都指向一个一等对象,该对象具有实例变量、可以有实例方法等。 - Sergey Kalinichenko
@msrd0 这样你就可以单独覆盖方法。我扩展了示例以显示覆盖 toString() - Sergey Kalinichenko
@dasblinkenlight 谢谢。我明白编译器所做的并不都像我的例子(A => public static final Member A)那样容易表达,这也是实例变量的情况。是的,我指的是静态方法。 - rents
显示剩余2条评论

2
“我是说,静态枚举常量似乎使用了非静态实例变量,这在通常情况下是不可能的?”
“我所说的‘通常情况’是指,在非枚举类中,静态变量无法访问非静态变量。”
仍然是正确的:静态变量无法访问非静态变量。在您的示例代码中,您没有这样做:没有静态变量访问非静态变量。
构造函数Members(int size)不在静态上下文中(构造函数从不在静态上下文中)。A、B和C都是枚举类型Members的实例,当创建这些实例时,将使用size参数调用构造函数。一旦构造完成,这些对象将被视为静态常量值。
也许另一个例子可以帮助理解:
class Person {
    private final String name;
    Person(String name) {
        this.name = name;
    }
}
class EmployeeDatabase {
    private static final Person CEO = new Person("Jack");
}

这里的EmployeeDatabase.CEO是一个具有非静态字段name的静态常量对象。 这就像Members.A一样,是一个具有非静态字段size的静态常量对象。

我想知道编译器如何给每个A、B、C提供size的副本。

与将构造函数参数传递给任何对象的方式完全相同。

您可以在文档中了解有关枚举的所有信息。


这段代码无法编译,size 不能是 final。 - msrd0
当然可以。不仅应该是final,而且必须是final。可变枚举将是非常薄弱的设计。 - janos
如果你有一个未初始化的 final 变量,javac 会不会显示错误?当一个 final 变量在构造函数中初始化时呢? - msrd0
@msrd0 他是在构造函数中进行初始化的。使用final声明的变量只能通过直接声明(private final int blub = 0;)或在构造函数中进行初始化。 - Tom
我不是在寻找这个。我更感兴趣的是编译器背后的实现。我已经知道如何使用枚举。我想知道编译器做了什么?(例如,如果我只是写A,编译器将其转换为“public static final Member A”。我想知道编译器如何给每个A、B、C分配一个大小的副本。) - rents

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接