skip to content

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?

level: middleimportance: should knowfreq 35%

answer

  1. Abstract member => every constant must override
  2. Constant body => anonymous subclass (synthetic Enum$N)
  3. Calls use virtual dispatch, no when
  4. Constant override beats class-level default
  5. Constants stay singletons; === still works

basics

~20 s

If 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 s

When 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 lines
kotlin
interface 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 dispatch

go deeper

for a junior

Knows an abstract method means each constant needs a body, but may not know about synthetic subclasses.

for a middle

Explains per-constant anonymous subclasses, virtual dispatch, and default-vs-override precedence.

for a senior

Discusses JVM synthetic class generation, identity preservation, and when per-constant beats centralized when.

for a principal

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

context