JLS的哪一部分表示匿名类不能拥有public / protected / private成员类

考虑这段代码:

public class TopLevelClass { Cloneable c = new Cloneable() { private int privateField; private void privateMethod() {}; }; } 

有一个匿名类,它有一个private成员字段和一个private成员方法。 它已成功编译。

然后考虑这个:

 public class TopLevelClass { Cloneable c = new Cloneable() { private class PrivateInnerClass {} }; } 

有一个匿名类,它有一个private成员类。 然而…

  • javac说: error: modifier private not allowed here
  • Eclipse说: Illegal modifier for the local class PrivateInnerClass; only abstract or final is permitted Illegal modifier for the local class PrivateInnerClass; only abstract or final is permitted 真正的本地课程?

什么? 为什么匿名类不能拥有publicprotectedprivate (以下简称those成员类,而他们可以拥有those成员字段和方法? 困惑,我看着JLS。 由于Eclipse的说法,我首先研究了本地课程 :

14.3。 本地类声明

本地类是一个嵌套类( §8 ),它不是任何类的成员,并且具有名称( §6.2 , §6.7 )。
如果本地类声明包含任何访问修饰符publicprotectedprivate (第6.6节 )或修饰符static (第8.1.1 节 ),则为编译时错误。

所以本地类不能有those修饰符。 但是PrivateInnerClass 匿名Cloneable的成员,因此它不是本地类,并且仍然能够拥有those修饰符。

然后我查看了类修饰符 :

8.1.1。 类修饰符

访问修饰符public (第6.6节 )仅适用于顶级类(第7.6节 )和成员类(第8.5节 ),不适用于本地类( 第14.3节 )或匿名类(第15.9.5节 )。
访问修饰符protectedprivate (第6.6节 )仅适用于直接封闭类或枚举声明(第8.5节 )中的成员类。

但是PrivateInnerClass是一个成员类,它位于一个直接封闭的类中,匿名Cloneable ,所以它仍然可以在理论上拥有those修饰符。 我也查看了其他部分,但我仍然找不到相关条款。

那么Java语言规范的哪一部分说匿名类的成员类不能拥有those修饰符?


额外注1:有些回答是关于成员类和本地类的,所以我做了一个测试,可以得出结论(除非修饰符很重要):

  1. 匿名Cloneable 既不是成员类也不是本地类
  2. PrivateInnerClass是一个成员类,但不是本地类

以下是我的测试代码:

 public class TopLevelClass { Cloneable c = new Cloneable() { class PrivateInnerClass {} }; public static void main(String[] args) throws ClassNotFoundException { Class c1 = Class.forName("TopLevelClass$1"); Class c2 = Class.forName("TopLevelClass$1$PrivateInnerClass"); System.out.println(c1.isMemberClass()); // false System.out.println(c1.isLocalClass()); // false System.out.println(c2.isMemberClass()); // true System.out.println(c2.isLocalClass()); // false } } 

额外注释2:审查正常类的声明( JLS§8.1 ):

 NormalClassDeclaration:
     ClassModifiers opt class Identifier TypeParameters opt
                                               超级选择接口选择 ClassBody

在我的理解中,当Identifier类是一个XXX类时,所述的§8.1.1限制了Identifier的修饰符,而不是Identifier ClassBody中其他声明中的修饰符。 否则,匿名类甚至不能拥有those成员字段和方法。

任何答案,特别是不同意额外注释2的答案,必须指出为什么允许those成员字段和方法。


额外注释3:如果您认为没有JLS的这一部分, 您仍需要提供可靠的文档来解释为什么禁止those成员类以及为什么允许those成员字段和方法。


你有:

  1. 顶级类TopLevelClass不嵌套(因此命名,不是本地,不是匿名)
  2. 二级类,一个扩展Clonable并且不是任何类的成员的无名类: 是匿名的(内部类,不是成员,在本地范围内,但不是“本地类”)
  3. 第三级类PrivateInnerClass ,匿名类的成员: 是嵌套的,不是本地的,不是匿名的,是非静态的内部类

您正在使用(2)中的修饰符private 。 您收录的JLS文字说明这是非法的:

8.1.1

访问修饰符public(第6.6节)仅适用于顶级类(第7.6节)和成员类(第8.5节), 不适用于本地类 (第14.3节) 或匿名类 (第15.9.5节)。 访问修饰符protected和private(第6.6节) 适用于直接封闭类或枚举声明(第8.5节)中的成员类

即你可以在匿名类的内部(在范围内)不使用这些修饰符。


回答额外注2:

在我的理解中,当Identifier类是一个XXX类时,所述的§8.1.1限制了Identifier的修饰符,而不是Identifier ClassBody中其他声明中的修饰符。 否则,匿名类甚至不能拥有那些成员字段和方法。

  1. 在标识符之前限制修饰符

    • 这在8.1.1中有详细说明。 它显然适用。
    • 所有修饰符都可以在成员类的标识符之前应用
    • public可以在顶级类Identifier之前应用
    • 在本地/匿名类的标识符( 在本地范围内声明的类)之前不能应用修饰符

    为什么是这样?

    因为成员类可以由其他类直接引用(通过顶级类中的“成员链”),但是本地/匿名类永远不能在外部引用 。 本地/匿名类声明隐藏在本身不能被java程序的任何其他部分访问的作用域中。

    当声明可供其他类访问时,修饰符在类声明之前是合法的。

  2. ClassBody中修饰符的限制

    如果Java程序的其他部分无法访问类标识符/声明,则当然也无法访问ClassBody。

    因此,只要修饰符在标识符之前是非法的,修饰符就不会在ClassBody中具有可能的语义含义。

    在ClassBody中是否允许修饰符的规则必须始终与在标识符之前是否允许修饰符的规则相同

  3. 所以8.1.1。 限制两个地方的修饰符

🙂

你错过了“包含”这个词。 PrivateInnerClass是您的匿名类的成员,它包含在其中,因此根据规则14.3,它本身不能具有访问修饰符。

它可以为自己的成员提供访问修饰符,但您还没有探索过。

Eclipse错误消息错误地将其描述为本地。

你也错过了’私人’即使它是合法的也不会增加任何东西这一点,因为内部阶级无论如何都是看不见的。

我的最终答案包括两个论点:

  1. JLS中没有强有力的匿名类成员修饰符声明。 即JLS没有这样的部分

  2. 但根据JVM规范, 匿名不是成员:

JVM 7规范: 4.7.6 InnerClasses属性状态:

如果C不是类或接口的成员(即,如果C是顶级类或接口(JLS§7.6)或本地类(JLS§14.3)或匿名类(JLS§15.9.5) ))…

所以,根据

8.5会员类型声明

成员类是一个类,其声明直接包含在另一个类或接口声明中。

匿名不是成员类

所以,根据8.1.1。 类修饰符

访问修饰符protected和private(第6.6节)仅适用于直接封闭类中的成员类

这个类不是成员类,所以他们不能提到修饰符