JLS 的哪一部分说匿名类不能有公共/受保护/私有成员类

Posted

技术标签:

【中文标题】JLS 的哪一部分说匿名类不能有公共/受保护/私有成员类【英文标题】:Which part of JLS said anonymous classes cannot have public/protected/private member classes 【发布时间】:2013-07-03 07:37:41 【问题描述】:

考虑这段代码:

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 真的是本地类吗?

什么? 为什么匿名类不能有publicprotectedprivate(以下简称those成员类却可以有those成员字段和方法? 很困惑,我调查了 JLS。由于 Eclipse 所说的,我首先查看了local classes:

14.3。本地类声明

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

所以本地类不能有those 修饰符。但是PrivateInnerClass 匿名Cloneable的成员,所以它不是本地类,仍然可以有those修饰符。

然后我查看了class modifiers:

8.1.1。类修饰符

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

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

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


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

    匿名Cloneable既不是成员类也不是本地类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):

普通类声明: ClassModifiersopt 类标识符 TypeParametersopt 超级opt 接口opt ClassBody

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

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


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


【问题讨论】:

随着时间的推移,您已经增加了答案的卷积。 @JoshDM 一半是为了展示我的“研究成果”。一半是告诉回答者“我发现了这些假证明,不要重复。” 【参考方案1】:

你有:

    ***类TopLevelClass没有嵌套(因此是命名的,不是本地的,不是匿名的) 二级类,无名类,扩展Clonable,不是任何类的成员:是匿名的(内部类,不是成员,在本地范围内但不是'本地类') 三级类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 中其他声明中的修饰符。否则,匿名类甚至不能拥有那些成员字段和方法。

    标识符前修饰符的限制

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

    这是为什么?

    因为成员类可以被其他类直接引用(通过***类的“成员链”),但本地/匿名类永远不能被外部引用。本地/匿名类声明隐藏在 Java 程序的任何其他部分本身无法访问的范围内。

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

    ClassBody 中修饰符的限制

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

    因此,只要修饰符在 Identifier 之前是非法的,修饰符在 ClassBody 中就没有可能的语义含义。

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

    所以 8.1.1。在两个地方都限制修饰符

:)

【讨论】:

请看我的编辑(附注 2)。有太多字不能评论。 1. 关于第二段有没有提到 JLS? 2. 为什么允许those 成员字段和方法? (例如我问题开头的代码)它们也无法访问,不是吗? 6.6: Qualified names are a means of access to members of packages and reference types. When the name of such a member is classified from its context (§6.5.1) as a qualified type name (denoting a member of a package or reference type, §6.5.5.2) or a qualified expression name (denoting a member of a reference type, §6.5.6.2), access control is applied. 6.6.1 A member (class, interface, field, or method) of a reference (class, interface, or array) 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. 重新字段和方法:同意,它们也无法访问。似乎是编译器不一致。它们应该包含在 6.6 中。 @GlenBest Something you might want to know about【参考方案2】:

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

它可以对其自己的成员具有访问修饰符,但您还没有探索过。

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

您也错过了“private”即使合法也不会添加任何内容的观点,因为无论如何内部类都是不可见的。

【讨论】:

§14.3:“...如果本地类声明包含...”。本地班在哪里?匿名类和PrivateInnerClass 都不是本地类。【参考方案3】:

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

    在 JLS 中没有对匿名类成员修饰符的严格限制声明。 IE。 JLS 中没有这样的部分

    但根据 JVM 规范匿名不是类的成员:

JVM 7 规范:4.7.6 The InnerClasses Attribute 声明:

如果 C 不是类或接口的成员(也就是说,如果 C 是 ***类或接口 (JLS §7.6) 或本地类 (JLS §14.3) 或匿名类(JLS §15.9.5))...

所以,根据

8.5 成员类型声明

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

匿名不是成员类

所以,根据 8.1.1。类修饰符

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

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

【讨论】:

@johnchen902 好吧,这是我的最终答案。你可以同意也可以不同意。我已经提到,JLS 没有这样的部分。 @johnchen902 此类类的任何成员都不能有任何提及的修饰符 @johnchen902 我对 §8.1.1 有什么误解? @johnchen902 只是为了澄清我已经更新了我的答案。匿名类不是成员类。 让我们continue this discussion in chat

以上是关于JLS 的哪一部分说匿名类不能有公共/受保护/私有成员类的主要内容,如果未能解决你的问题,请参考以下文章

可以通过反射类访问的公共,私有,受保护类有啥用? [复制]

打字稿:使父类中的公共方法成为派生类中的私有/受保护方法

为啥类或接口不能接收私有或受保护的访问修饰符?

公共/受保护/私有继承的问题

Objective-C - 私有与受保护与公共

如何禁止公共继承但允许私有(和受保护)继承