为什么我不能把enum的内部课程公开?

Jua*_*olo 5 java enums java-8

测试了一些东西我尝试做一个枚举,其中枚举中的每个元素都有一个不同的类.

举个例子:

public enum MyEnum {

    first{
        class First{}
    },
    second {
        class Second{}
    };
}
Run Code Online (Sandbox Code Playgroud)

如果我尝试在任何类之前放置一个公共修饰符,则此处不允许使用修饰符.我不太清楚为什么会这样.我不能在枚举之外实例化这些类,也不能看到它们.但是我可以设法让实例这样做:

public enum MyEnum {

    first{
        class First{}

        public Object getObject(){
            return new First();
        }
    },
    second {
        class Second{}

        public Object getObject(){
            return new Second();
        }
    };

    public abstract Object getObject();
}


public class Main {
    public static void main(String[] args) {
        System.out.println(MyEnum.first.getObject().getClass());
        System.out.println(MyEnum.second.getObject().getClass());
    }
}
Run Code Online (Sandbox Code Playgroud)

随着输出:

class MyEnum $ 1 $ First

class MyEnum $ 2 $秒

我可以清楚地引用这个类,为什么我不能在编译时访问它?

Hol*_*ger 5

TL;DR 这已通过 JDK\xc2\xa016 修复
\n

JDK\xc2\xa016 更改了规则,现在允许内部类拥有static成员。看来这个问题的问题已作为副作用得到修复。由于这也应该是早期版本中的行为,public因此即使使用--release 8.

\n

这是一个非常有趣的问题。您将无法在编译时访问这些类,即使public,因为它们包含在隐式匿名类中,因此无论如何都不能通过名称访问它们(除了在匿名类中) )。您可以\xe2\x80\x99t通过变量访问类型,即MyEnum.first.First在Java中根本不可能访问。

\n

尽管如此,没有用并不一定决定可以声明什么,即在一个public内部类中声明一个内部类。private外部类中声明内部类也是可能的。正式规则是相关的,虽然乍一看似乎是预期的行为,但令人惊讶的是它并没有以这种方式得到规范的支持。

\n

JLS \xc2\xa78.9.1,枚举常量指出:

\n
\n

枚举常量的可选类主体隐式定义匿名类声明 ( \xc2\xa715.9.5 ),该声明扩展了直接封闭的枚举类型。类体由匿名类的通常规则\xe2\x80\xa6管理

\n
\n

这给了我们一个有趣的提示,即

\n
class Outer {\n    static Object o = new Object() {\n        public class Inner {\n        }\n    };\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在 JDK\xc2\xa016 之前,也会被编译器拒绝。

\n

考虑到JLS,\xc2\xa78.1.1。类修饰符

\n
\n

访问修饰符public( \xc2\xa76.6 ) 仅适用于顶级类 ( \xc2\xa77.6 ) 和成员类 ( \xc2\xa78.5 ),不适用于本地类 ( \xc2\xa714.3 ) 或匿名类(\xc2\xa715.9.5)。

\n
\n

我们必须决定Inner您属于哪个类别或First类别。\xe2\x80\x99 与它们周围的类是匿名类这一事实无关。显然,它们既不是顶级类,也不是匿名类,因为它们是嵌套的并且有名称。所以它们必须是成员类(public允许)或本地类(public不允许)。

\n

JLS,\xc2\xa78.5。成员类型声明

\n
\n

成员是其声明直接包含在另一个类或接口声明的主体中的类(\xc2\xa78.1.6\xc2\xa79.1.4)。

\n
\n

另一个类\xe2\x80\xa6声明\xe2\x80\x9d的\xe2\x80\x9cbody是通过指向\xc2\xa78.1.6来定义的,它确实定义了ClassBody语言语法,通常用于命名声明匿名声明类enum常量;它们都指向\xc2\xa78.1.6的\xe2\x80\x9cclass body\xe2\x80\x9d。考虑到这一点,我们的类是\xe2\x80\x9c成员类\xe2\x80\x9d,因为它们包含在类主体中。

\n

现在我们可以尝试将其解释为错误的交叉引用,假设另一个类的 \xe2\x80\x9cbody声明\xe2\x80\xa6 是指引用类声明,即使用命名类声明然而,关键字局部类class的定义反驳了这种解释。

\n

JLS \xc2\xa714.3,本地类

\n
\n

本地是一个嵌套类 ( \xc2\xa78 (Classes) ),它不是任何类的成员,并且具有名称 ( \xc2\xa76.2\xc2\xa76.7 )。

\n

\xe2\x80\xa6

\n

每个局部类声明语句立即包含在一个块(\xc2\xa714.2)中。局部类声明语句可以与块中的其他类型的语句自由混合。

\n
\n

abstract\xe2\x80\x9cBlock\xe2\x80\x9d 的真正含义是类似于非方法、构造函数或初始化器的定义的块( \xc2\xa714.2 )。这不适用于Inner上述班级或您的First班级Second。它们没有放置在块中,并且不能与该上下文中的语句自由混合,因为此时不允许使用语句。

\n

换句话说,它们绝对不是本地类,并且假设没有规范中未描述的类的其他类别,我们必须将它们视为成员类,正如成员类定义的当前编写和链接也表明的那样,也就是说,根据引用的规则,public该处应该允许修饰符

\n

为了完整起见,这里是匿名类的定义,只是为了表明没有例外规则说它们的成员类不允许\xe2\x80\x99t public

\n
\n

15.9.5。匿名类声明

\n

匿名类声明是由 Java 编译器自动从类实例创建表达式派生的。

\n

匿名类永远不是abstract(\xc2\xa78.1.1.1)。

\n

匿名类始终是隐式的final(\xc2\xa78.1.1.2)。

\n

匿名类始终是内部类(\xc2\xa78.1.3);它永远不是static(\xc2\xa78.1.1,\xc2\xa78.5.1)。

\n
\n

最后一点意味着,反过来,它们的成员类可以是\xe2\x80\x99 static,但是,没有规则禁止它们是public

\n