javaenumsjvmjava-synthetic-methods

Are enum instances "enclosed" in the enum type in java?


Reproducer :

enum IDs {
    ID {

        @Override
        void getId() {
            w(); // warning here
        }
    };

    void getId() {}

    private static void w() {}
}

Warning emitted :

Access to enclosing method w() from the type IDs is emulated by a synthetic accessor method

I understand what synthetic methods are - what I do not get is how they come into play with enums - I would expect enum instances to have all private methods I define in the enum. Are instances really nested classes ?


Solution

  • An enum instance which defines methods, as your ID does here, is a singleton of an implicit anonymous subclass of the enum class. The normal access rules apply between the subclass and the enum class, so a synthetic accessor is required to see private features of the enum class.

    The Java Language Specification requires enums to work this way:

    The optional class body of an enum constant implicitly defines an anonymous class declaration (§15.9.5) that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes...

    It's certainly how they're actually implemented. In the JDK's javac, this happens in JavacParser::enumeratorDeclaration around line 3344 (in this version):

    JCClassDecl body = null;
    if (token.kind == LBRACE) {
        JCModifiers mods1 = F.at(Position.NOPOS).Modifiers(Flags.ENUM | Flags.STATIC);
        List<JCTree> defs = classOrInterfaceBody(names.empty, false);
        body = toP(F.at(identPos).AnonymousClassDef(mods1, defs));
    }
    if (args.isEmpty() && body == null)
        createPos = identPos;
    JCIdent ident = F.at(identPos).Ident(enumName);
    JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body);
    

    The relevant bits there are that if there is a left curly bracket (LBRACE) in the declaration, then a class body is parsed (classOrInterfaceBody(...)) for an anonymous class (names.empty), and this is then used as the class body in an instance creation expression (NewClass(..., body)). You can follow through the compilation of JCNewClass nodes if you like, but it suffices to say, as its javadoc does, that it models:

     * A new(...) operation.
    

    And as you know, a new operation with a class body creates an anonymous class.