Explain how abstract members and per-constant bodies interact when an enum implements an interface. What does the compiler generate, and how does dispatch work?
answer
- Abstract member => every constant must override
- Constant body => anonymous subclass (synthetic Enum$N)
- Calls use virtual dispatch, no when
- Constant override beats class-level default
- Constants stay singletons; === still works
basics
~20 sIf the enum leaves an interface method abstract, every constant must give it a body. The compiler makes each such constant its own hidden subclass, so calling the method picks the right constant's version automatically.
solid answer
~50 sWhen an enum implements an interface but does **not** provide a class-level implementation, the member stays **abstract**, which forces every constant to supply a body overriding it. The compiler turns each constant with a body into an **anonymous subclass** of the enum (on the JVM these compile to synthetic classes like `Enum$1`), and the singleton constant instance is of that subclass. Calls are ordinary **virtual dispatch**: invoking the interface method on a constant runs that constant's override. You can also declare an explicit `abstract fun` inside the enum to require per-constant behavior even when no interface demands it. If you provide a class-level override AND a constant body, the constant body wins for that constant (it's a normal override). This is the canonical 'strategy per value' pattern: behavior lives next to each constant rather than in a central `when`.
code
kotlin · 7 linesinterface Op { fun apply(a: Int, b: Int): Int }
enum class Arith : Op {
PLUS { override fun apply(a: Int, b: Int) = a + b },
TIMES { override fun apply(a: Int, b: Int) = a * b };
}
// PLUS and TIMES compile to synthetic subclasses Arith$1, Arith$2.
// val op: Op = Arith.TIMES; op.apply(2,3) -> 6 via virtual dispatchgo deeper
Knows an abstract method means each constant needs a body, but may not know about synthetic subclasses.
Explains per-constant anonymous subclasses, virtual dispatch, and default-vs-override precedence.
Discusses JVM synthetic class generation, identity preservation, and when per-constant beats centralized when.
Weighs the design trade-off (behavior locality vs. class proliferation) and ties it to maintainability and open/closed concerns.
## Abstract members force per-constant bodies If the enum declares (or inherits, without implementing) an `abstract fun`, the compiler requires **every constant** to override it: ```kotlin enum class Direction { NORTH { override fun opposite() = SOUTH }, SOUTH { override fun opposite() = NORTH }, EAST { override fun opposite() = WEST }, WEST { override fun opposite() = EAST }; abstract fun opposite(): Direction } ``` The same rule applies when the `abstract` member comes from an **interface** the enum implements without a class-level override. ## What the compiler generates Each constant **with a body** becomes an **anonymous subclass** of the enum class. On the JVM these are synthetic classes (e.g. `Direction$1`, `Direction$2`). The public constant (`Direction.NORTH`) is a singleton instance of its subclass. Constants **without** a body are plain instances of the enum class itself. ## Dispatch Calling `someDirection.opposite()` is normal **virtual (polymorphic) dispatch**: the JVM looks up the override on the actual runtime subclass of that constant. No `when`/`if` is involved. This is why the enum can be passed around as the interface type and still behave correctly. ## Default + override precedence ```kotlin interface Sound { fun noise(): String } enum class Animal : Sound { DOG, // uses class-level default CAT { override fun noise() = "Meow" }; // overrides override fun noise() = "..." // class-level default } ``` `DOG.noise()` -> "..."; `CAT.noise()` -> "Meow". A constant body override always wins over the class-level default for that constant — standard override semantics. ## Practical consequences - Per-constant bodies add synthetic classes (slightly more `.class` files); negligible in practice. - Because each is a singleton subclass, identity (`===`) and exhaustive `when` still work. - Prefer per-constant overrides over a centralized `when` when behavior is intrinsic to each value.
- What happens if you forget to give one constant a body for an abstract member?Compile error: the constant must implement the abstract member; the enum won't compile until every constant overrides it (or you provide a class-level default).
- Do constants without bodies create extra synthetic classes?No — only constants with bodies become anonymous subclasses; body-less constants are direct instances of the enum class.
An abstract enum method is like a job posting every constant must fill out; each constant ends up as its own specialized team member doing the work.
saying these in an interview costs you the question
- Thinking the compiler uses a hidden when/switch for dispatch
- Saying all constants are instances of the same exact class even with bodies
- Believing a class-level default removes the need for any constant override
- Claiming abstract members are optional for some constants
- Not knowing constant-body overrides win over the default