11

考虑这段代码:

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 所说的,我首先查看了本地类

14.3. 本地类声明

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

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

然后我查看了类修饰符

8.1.1。类修饰符

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

ButPrivateInnerClass是一个成员类,并且它在一个直接封闭的类中,匿名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):

普通类声明:
    ClassModifiers opt class Identifier TypeParameters opt 
                                               Super opt Interfaces opt ClassBody

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

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


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


4

3 回答 3

7

你有:

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

您正在使用private(2) 中的修饰符。您包含的 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可以在顶级类标识符之前应用
    • 在本地/匿名类的标识符(在本地范围内声明的类)之前不能应用修饰符

    为什么是这样?

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

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

  2. ClassBody 中修饰符的限制

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

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

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

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

:)

于 2013-07-07T12:37:42.597 回答
3

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

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

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

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

于 2013-07-06T02:16:05.100 回答
2

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

  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 节)仅适用于直接封闭类中的成员类

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

于 2013-07-05T10:57:40.893 回答