Is an enum constant-specific class body static or non-static?

Well this is a strange case.

It appears that the problem is:

  • In this case, the private member should be accessible (6.6.1.):

    Otherwise, the member or constructor is declared private, and access is permitted if and only if it occurs within the body of the top level class that encloses the declaration of the member or constructor.

  • However, private members are not inherited (8.2):

    Members of a class that are declared private are not inherited by subclasses of that class.

  • Therefore, printMe is not a member of the anonymous subclass and the compiler searches for it within the superclass* Operation (15.12.1):

    If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.

    This search policy is called the “comb rule”. It effectively looks for methods in a nested class’s superclass hierarchy before looking for methods in an enclosing class and its superclass hierarchy.

  • And here is where it gets strange. Because printMe is found in a class that also encloses PLUS, the object that the method is called on is instead determined to be an enclosing instance of Operation, which doesn’t exist (15.12.4.1):

    Otherwise, let T be the enclosing type declaration of which the method is a member, and let n be an integer such that T is the n‘th lexically enclosing type declaration of the class whose declaration immediately contains the method invocation. The target reference is the n‘th lexically enclosing instance of this.

    It is a compile-time error if the n‘th lexically enclosing instance of this does not exist.

So in short, because printMe is only a member of Operation (and not inherited), the compiler is compelled to invoke printMe on a non-existent outer instance.

However, the method is still accessible and we can find it by qualifying the invocation:

@Override
double apply(double x, double y) {
//  now the superclass is searched
//  but the target reference is definitely 'this'
//  vvvvvv
    super.printMe(x);
    return x + y;
}

The two error messages sound contradictory to each other […].

Yes, this is a confusing aspect of the language. On the one hand, an anonymous class is never static (15.9.5), on the other hand, an anonymous class expression can appear in a static context and therefore has no enclosing instance (8.1.3).

An anonymous class is always an inner class; it is never static.

An instance of an inner class I whose declaration occurs in a static context has no lexically enclosing instances.

To help understand how this works, here is a formatted example:

class Example {
    public static void main(String... args) {
        new Object() {
            int i;
            void m() {}
        };
    }
}

Everything in italics is the static context. The anonymous class derived from the expression in bold is considered inner and non-static (but has no enclosing instance of Example).

Since the anonymous class is non-static it cannot declare static non-constant members, despite that it is itself declared within a static context.


* Besides obscuring the matter a little, the fact that Operation is an enum is completely irrelevant (8.9.1):

The optional class body of an enum constant implicitly defines an anonymous class declaration that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes […].

Leave a Comment