Java Enums: Why Abstract Methods Work, and the Anonymous Subclass Behind Every Constant
Code that compares enum values by `getClass()` will break for constants that define class bodies, since those check the anonymous subclass rather than the enum type. The difference also affects serialization, logging, and any reflection that relies on `isEnum()` or `getEnumConstants()`, which return false and null respectively for those anonymous classes.
The Java Language Specification allows an abstract method inside an enum, and every constant with a class body becomes an anonymous final subclass of the enum type. The abstract method's implementation lives in those constant bodies, so calling `PLUS.eval(1, 2)` actually invokes a method on `Operation$1`, not on `Operation` itself.
The behavior shows up in the reflection API: for constants with class bodies, `getClass()` returns a compiler-generated name like `Operation$3`, while `getDeclaringClass()` always returns the enum type. JDK's own `Enum.compareTo` relies on this distinction. Empty class bodies still produce subclasses, and any enum with at least one such constant is implicitly sealed, with the permitted subclasses being exactly those anonymous classes.
A Swing-based GUI explorer built for this post walks through three sample enums: one with no class bodies, one where every constant has a body, and one with a mix. The tool displays the hierarchy, the source with the selected constant highlighted, and a nine-row fact table comparing `getClass()` and `getDeclaringClass()` for each constant.
The subtlety that an empty class body still produces a distinct subclass is easy to miss; it means any constant that even marks a body changes the runtime type, which can surprise code doing strict class checks.
The compiler marks the enum class abstract when an abstract method is present, but the JLS grants an exception to the normal rule that abstract methods require an abstract class, so the enum itself stays instantiable through constants.
The GUI tool's emphasis on comparing getClass() with getDeclaringClass() is a practical debugging aid for anyone who has seen unexpected Operation$1 names in logs or serialized data.