关于java:默认情况下,枚举中变量的访问级别是多少

What is the access level of variables in enums by default

本问题已经有最佳答案,请猛点这里访问。

最近我遇到了以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Animals {
    DOG("woof"), CAT("meow"), FISH("burble");
    String sound;

    Animals(String s) {
        sound = s;
    }
}

class TestEnum {
    static Animals a;
    public static void main(String[] args) {
        System.out.println(a.DOG.sound +"" + a.FISH.sound);//Expected compilation failure
    }
}

我希望代码由于这个a.DOG.sound部分而无法编译。但令我惊讶的是,它没有。我搜索了所有的地方,包括官方文件,以找出访问级别,但没有发现任何东西。是公开的还是违约的?


If you can import enum you can access enum constants

如果在package外可以访问enum(特别声明为public),则其元素也可以访问,如果没有指定修改器,则只能在package内访问。默认情况下,如果enum是可访问的,则可以访问enum常量,这意味着默认情况下这些常量是public static final

I would expect the code to fail to compile because of this a.DOG.sound
part. But to my surprise it doesn't.

它将与任何其他变量在任何类中的行为相同,如果没有默认修饰符,则只能在包内访问它。


枚举中手动声明的字段的隐式访问级别是package private,与普通类中完全相同。因此,只有当且仅当AnimalsTestEnum在同一包中时,您的sound字段才可访问。

我试图在jls中找到一个可靠的引用,但很不幸,枚举规则分散在各处,被指定为普通类规则的例外,因此规则必须从片段中组装。JLS第6.6.1节"确定可接近性"规定:

A member (class, interface, field, or method) of a reference type, or a constructor of a class type, is accessible only if the type is accessible and the member or constructor is declared to permit access:

  • If the member or constructor is declared public, then access is permitted.

    All members of interfaces lacking access modifiers are implicitly public.

  • Otherwise, if the member or constructor is declared protected, then access is permitted only when one of the following is true:

    • Access to the member or constructor occurs from within the package containing the class in which the protected member or constructor is declared.

    • Access is correct as described in §6.6.2.

  • Otherwise, if the member or constructor is declared with package access, then access is permitted only when the access occurs from within the package in which the type is declared.

    A class member or constructor declared without an access modifier implicitly has package access.

  • Otherwise, the member or constructor is declared private, and access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.

这意味着类类型(classenum获得成员隐式具有包访问权的规则,而接口类型(interface@interface获得成员隐式公共的规则。

从上面看,"class-member"在其"class"的定义中包含枚举并不是很明显,但它确实包含枚举。由于它们的广泛重叠,jls组在许多地方使用类枚举(注释类型也使用接口分组)。jls§8.9枚举类型表示"枚举声明指定了新的枚举类型,一种特殊的类类型";jls§8.2类成员明确表示,"类成员"一词是指"类类型"的成员。

但是,Enums确实有两个关于成员可访问性的特殊规则,这些规则不包括在上面引用的部分中:

  • 枚举常量本身(在您的示例中,它们是DOGCATFISH)可能没有任何显式访问修饰符(jls§8.9.1),并且始终是枚举类型(jls§8.9.3)的public static final字段。

  • 枚举构造函数必须是私有的(以防止人们创建额外的常量),并且是隐式私有的(jls§8.9.2)。

  • 除了这两个例外之外,普通类的访问规则也适用于枚举。如果您的Animals枚举被设置为public枚举,那么它及其所有常量都可以在包外部访问,但是sound字段是包私有字段,除非您明确声明它public否则不能在包外部访问。