Which part of JLS said anonymous classes cannot ha

2020-07-04 05:15发布

问题:

Consider this piece of code:

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

There is an anonymous class that has a private member field and a private member method. It has been successfully compiled.

Then consider this one:

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

There is an anonymous class that has a private member class. However...

  • javac said: error: modifier private not allowed here
  • Eclipse said: Illegal modifier for the local class PrivateInnerClass; only abstract or final is permitted Really local class?

What? Why anonymous classes cannot have public, protected or private(hereinafter referred to as those) member classes while they can have those member fields and methods? Confused, I looked into JLS. Because of what Eclipse stated, I looked into local classes first:

14.3. Local Class Declarations

A local class is a nested class (§8) that is not a member of any class and that has a name (§6.2, §6.7).
It is a compile-time error if a local class declaration contains any of the access modifiers public, protected, or private (§6.6), or the modifier static (§8.1.1).

So local class cannot have those modifiers. But PrivateInnerClass is a member of the anonymous Cloneable, so it is not a local class and is still able to have those modifiers.

Then I looked into class modifiers:

8.1.1. Class Modifiers

The access modifier public (§6.6) pertains only to top level classes (§7.6) and to member classes (§8.5), not to local classes (§14.3) or anonymous classes (§15.9.5).
The access modifiers protected and private (§6.6) pertain only to member classes within a directly enclosing class or enum declaration (§8.5).

But PrivateInnerClass is a member class, and it's within a directly enclosing class, the anonymous Cloneable, so it can still have those modifiers on theory. I looked into other parts as well, but I still couldn't find relevant provisions.

So which part of Java Language Specification said a member class of an anonymous class cannot have those modifier?


Extra Note 1: Some answer argued about member classes and local classes, so I made a test that can conclude that (unless modifiers matters):

  1. The anonymous Cloneable is neither a member class nor a local class.
  2. The PrivateInnerClass is a member class, but not a local class.

The following is my test code:

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
    }
}

Extra Note 2: Review the declaration of a normal class (JLS §8.1):

NormalClassDeclaration:
    ClassModifiersopt class Identifier TypeParametersopt
                                               Superopt Interfacesopt ClassBody

In my understanding, when the Identifier class is an XXX class, what §8.1.1 stated is restricting the modifier of Identifier, not the modifiers in other declarations in ClassBody of Identifier. Otherwise, anonymous classes even cannot have those member fields and methods.

Any answer, especially which disagree with Extra Note 2, must point out why those member fields and methods are allowed.


Extra Note 3: If you think there isn't such part of JLS, you'll still need to give a reliable document to explain why those member classes are forbidden and why those member fields and methods are allowed.


回答1:

You have:

  1. Top-Level Class TopLevelClass : not nested (hence is named, not local, not anonymous)
  2. Second-Level Class, a no-name class that extends Clonable and is a not a member of any class: is anonymous (an inner class, is not a member, is in local scope but is not a 'local class')
  3. Third-Level Class PrivateInnerClass, a member of the anonymous class: is nested, is not local, is not anonymous, is a non-static inner class

You are using the modifier private in (2). Your included JLS text spells out this is illegal:

8.1.1

The access modifier public (§6.6) pertains only to top level classes (§7.6) and to member classes (§8.5), not to local classes (§14.3) or anonymous classes (§15.9.5). The access modifiers protected and private (§6.6) pertain only to member classes within a directly enclosing class or enum declaration (§8.5).

i.e. you can use none of these modifiers inside (in the scope of) an anonymous class.


Answer to Extra Note 2:

In my understanding, when the Identifier class is an XXX class, what §8.1.1 stated is restricting the modifier of Identifier, not the modifiers in other declarations in ClassBody of Identifier. Otherwise, anonymous classes even cannot have those member fields and methods.

  1. Restriction of modifier before Identifier

    • This is spelled out in 8.1.1. It clearly applies.
    • All modifiers can be applied before Identifier of a member class
    • public can be applied before top-level class Identifier
    • No modifiers can be applied before Identifier of local/anonymous classes (classes declared in local scope)

    Why is this?

    Because member classes can be referenced directly by other classes (through a 'member chain' from the top-level class), but local/anonymous classes can never be referenced externally. Local/Anonymous class declarations are hidden in a scope that is itself not accessible to any other part of the java program.

    Modifiers are only legal before a class declaration when the declaration is accessible to other classes.

  2. Restriction of modifier within ClassBody

    If a class Identifier/Declaration is not accessible to other parts of the java program, of course, the ClassBody is not accessible either.

    Hence, whenever a modifier is illegal before the Identifier, a modifier could have no possible semantic meaning within the ClassBody.

    The rules for whether a modifier is allowed within ClassBody must always be identical to the rules for whether a modifier is allowed before Identifier.

  3. So 8.1.1. restricts modifiers in both places

:)



回答2:

You've missed the word 'contain'. PrivateInnerClass is a member of your anonymous class and it is contained within it, so it cannot itself have an access modifier, under rule 14.3.

It can have access modifiers for its own members, but you haven't explored that.

The Eclipse error message wrongly describes it as local.

You've also missed the point that 'private' would add nothing even if it were legal, as the inner class is invisible anyway.



回答3:

My final answer consists of two thesis:

  1. There is not strong declaration of restrictions for anonymous class members modifiers in the JLS. I.e. there isn't such part of JLS.

  2. But according to JVM specs anonymous classes aren't members of class:

JVM 7 spec: 4.7.6 The InnerClasses Attribute states:

If C is not a member of a class or an interface (that is, if C is a top-level class or interface (JLS §7.6) or a local class (JLS §14.3) or an anonymous class (JLS §15.9.5))...

so, according to

8.5 Member Type Declarations

A member class is a class whose declaration is directly enclosed in another class or interface declaration.

anonymous classes are not member classes.

so, according to 8.1.1. Class Modifiers:

The access modifiers protected and private (§6.6) pertain only to member classes within a directly enclosing class

this classes aren't member classes, so they can not have mentioned modifiers.