封闭类中的私有枚举和静态字段

Fil*_*erg 7 java enums static

我理解为什么枚举构造函数不能访问枚举本身中的静态字段和方法,以及为什么在类中允许这样做.以下是代码示例,

import java.util.ArrayList;
import java.util.List;

public enum Foo {
    A("Some string"),
    B("Some other string"),
    ;

    static List<String> list = new ArrayList<>();

    Foo(String description) {
        list.add(description);
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码导致编译时错误illegal reference to static field from initializer.

相关背景

在静态字段全部初始化之前调用枚举构造函数.在上面的例子中,这意味着list尚未初始化.这是因为静态字段按文本顺序初始化(第12.4.2节)

接下来,按文本顺序执行类的类变量初始值设定项和类的静态初始值设定项,或接口的字段初始值设定项,就好像它们是单个块一样.

(强调我的)

并且因为枚举值本身总是在任何其他字段(包括静态字段)之前,所以它们不可用于枚举构造函数,即枚举值之前没有静态字段A,并且B.

但是,这是我的问题,为什么"私有"(包含在类中)enum 可以访问其封闭类的静态字段,无论枚举是否出现在---或---静态之后字段?特别是,在Java规范中指定了哪个?

请参阅以下代码以供参考

import java.util.ArrayList;
import java.util.List;

public class Bar {
    static List<String> first = new ArrayList<>();

    enum Baz {
        A("Some string"),
        B("Some other string"),
        ;


        Baz(String description) {
            // Can access static fields from before the enum
            first.add(description);

            // Can access static fields from _after_ the enum
            second.add(description);
        }
    }

    static List<String> second = new ArrayList<>();
}
Run Code Online (Sandbox Code Playgroud)

Sot*_*lis 5

JLS 中这种情况随处可见。当初始化发生时章节指出

\n\n
\n

其目的是类或接口类型具有一组初始值设定项,\n 将其置于一致的状态,并且该状态是其他类观察到的第一个\n 状态。初始static值设定项和\n 类变量初始值设定项按文本顺序执行,并且不能\n 引用其声明在使用后\n 以文本形式出现的类中声明的类变量,即使这些类变量\n 在范围内(\xc2 \xa78.3.3)。此限制旨在在编译时检测大多数循环或其他格式错误的初始化。

\n
\n\n

该粗体代码片段指的是直接包含访问权限的类。

\n\n

enum类型在 Java 语言规范中定义,此处

\n\n
\n

枚举声明指定一个新的枚举类型,一种特殊的类类型。

\n
\n\n

Baz您在构造函数 中访问的字段

\n\n
Baz(String description) {\n    // Can access static fields from before the enum\n    first.add(description);\n\n    // Can access static fields from _after_ the enum\n    second.add(description);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

不是类中声明的类变量,枚举类型Baz。因此允许访问。

\n\n

我们可以更深入地了解详细的类初始化过程,它解释了每个类(类、接口、枚举)都是独立初始化的。但是,我们可以创建一个示例来查看尚未初始化的值

\n\n
public class Example {\n    public static void main(String[] args) throws Exception {\n        new Bar();\n    }\n}\n\nclass Bar {\n    static Foo foo = Foo.A;\n    static Integer max = 42;\n\n    enum Foo {\n        A;\n\n        Foo() {\n            System.out.println(max);\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这将打印null. 尽管我们的程序执行在初始化之前就达到了访问权限,但max在类型的构造函数中允许访问。JLS对此发出警告enummax

\n\n
\n

事实上,初始化代码不受限制,允许构造示例,其中在评估其初始化表达式之前,当类变量仍具有初始默认值时,可以观察类变量的值,但这样的示例很少见实践。(也可以为实例变量初始化(\xc2\xa712.5)构造这样的示例。)在这些初始化器中可以使用 Java 编程语言的全部功能;程序员必须小心谨慎

\n
\n\n
\n\n

您的原始示例引入了一个额外的规则,该规则在“枚举主体声明”一章Foo中定义。

\n\n
\n

从枚举类型的构造函数、实例初始值设定项或实例变量初始值设定项表达式引用枚举类型的静态字段是编译时错误,除非该字段是常量变量 (\xc2\xa74 .12.4)。

\n
\n\n

该规则会阻止您的Foo代码片段编译。

\n\n

enum常量转换为public static final字段。它们在文本上首先出现在enum类型定义中,因此首先被初始化。它们的初始化涉及构造函数。这些规则的存在是为了防止构造函数看到稍后必须初始化的其他类变量的未初始化值。

\n