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

时间:2017-05-04 02:15:55

标签: java enums java-8

测试了一些东西,我试着制作一个枚举,其中枚举中的每个元素都有不同的类。

以例如:

public enum MyEnum {

    first{
        class First{}
    },
    second {
        class Second{}
    };
}

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

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());
    }
}

输出:

  

class MyEnum $ 1 $ First

     

class MyEnum $ 2 $ Second

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

2 个答案:

答案 0 :(得分:4)

这是一个非常有趣的问题。即使允许public修饰符,您也无法在编译时访问这些类,因为它们包含在隐式匿名类中,因此无论如何都无法通过名称访问它们(除了匿名之外)类)。您无法通过变量访问类型,即在Java中根本无法访问MyEnum.first.First

但是,没有用是不一定确定可以声明的内容,即在public外部类中声明private内部类也是可能的。正式规则是相关的,虽然它看起来像乍看之下的预期行为,但令人惊讶的是它没有以这种方式支持规范。

JLS §8.9.1, Enum Constants州:

  

枚举常量的可选类主体隐式定义了一个匿名类声明(§15.9.5),它扩展了直接封闭的枚举类型。班级团体由匿名班级的通常规则管理......

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

class Outer {
    static Object o = new Object() {
        public class Inner {
        }
    };
}

被编译器拒绝。

考虑JLS, §8.1.1. Class Modifiers

  

访问修饰符public§6.6)仅适用于顶级类(§7.6)和成员类(§8.5),而不属于本地类({{3} }}或匿名类(§14.3)。

我们必须决定哪个类别Inner或您的First类失败。这并不是因为他们周围的班级是一个匿名班级。显然,它们既不是顶级类也不是匿名类,因为它们是嵌套的并且具有名称。所以它们必须是,成员类(允许public)或本地类(public不允许)。

§15.9.5

  

成员类是一个类,其声明直接包含在另一个类或接口声明(JLS, §8.5. Member Type Declarations§8.1.6)的主体中。

“另一个类......声明的主体”是通过指向§9.1.4来定义的,§8.1.6确实定义ClassBody语言语法,named declarations常用,anonymous classesenum constant bodies;所有这些都指向§8.1.6的“阶级主体”。考虑到这一点,我们的类是“成员类”,因为它们包含在类体中。

现在我们可以尝试将其解释为错误的交叉引用,假设“另一个类的主体...... 声明”意味着引用Class Declarations,即命名类声明使用class关键字,但本地类的定义反驳了这种解释。

JLS §14.3, Local Classes

  

本地类是一个嵌套类(§8 (Classes)),它不是任何类的成员,并且具有名称(§6.2§6.7

     

...

     

每个本地类声明语句都会立即被一个块(§14.2)包含。本地类声明语句可以与块中的其他类型的语句自由混合。

“Block”实际上意味着块类似于非abstract方法,构造函数或初始值设定项(§14.2)的定义。这不适用于上面的Inner课程或您的FirstSecond课程。它们不是放在一个块中,也不能在该上下文中与语句自由混合,因为此时不允许声明。

换句话说,它们肯定不是本地类,并且假设没有其他类没有规范描述的类,我们必须将它们视为成员类,作为当前的编写和链接成员类的定义也表示,换句话说,根据引用的规则,应该允许public修饰符在这个地方

为了完整性,这里是匿名类的定义,只是为了表明没有例外规则说他们的成员类不允许public

  15.9.5. Anonymous Class Declarations      

Java编译器会自动从类实例创建表达式派生出一个匿名类声明。

     

匿名类永远不会是abstract(§8.1.1.1)。

     

匿名类总是隐式final(§8.1.1.2)。

     

匿名类始终是内部类(第8.1.3节);它永远不会static(§8.1.1,§8.5.1)。

最后一点暗示,他们的成员类也不能static,但是,没有规则禁止他们成为public

答案 1 :(得分:3)

您正在使用当前方法对MyEnum进行子类化({}语法创建一个匿名子类),它可以工作但看起来过于复杂。你拥有的东西等同于

public enum MyEnum {
    first, second; // <-- convention would be FIRST, SECOND
    public static class First { // <-- can be public.
    }
    public static class Second {
    }
    public Object getObject() {
        if (this == first) {
            return new First();
        }
        return new Second();
    }
}