skip to content

When designing a type hierarchy, when would you choose an abstract class over an interface in Kotlin, given interfaces can have default method implementations?

level: seniorimportance: should knowfreq 55%

answer

  1. abstract class: state + constructor, single inheritance
  2. interface: no backing field, no constructor, many implemented
  3. interface = capability/mix-in; abstract = is-a base with state
  4. diamond resolved with super<Type>
  5. hybrid: interface contract + AbstractXxx base

basics

~10 s

Choose an abstract class when you need shared stored state, a constructor, or want to allow only one parent. Choose an interface when you want a contract many unrelated types can implement and combine.

solid answer

~50 s

Abstract classes and interfaces overlap because both can declare abstract members and provide concrete behavior, but they differ in capability. Abstract classes can hold state in backing fields, have constructors with parameters and init blocks, control initialization order, and a class may extend only one of them (single inheritance). Interfaces cannot store state in backing fields (properties have no backing field, only abstract or computed via getters), have no constructor, and a class can implement many of them (mix-ins). So pick an abstract class for an 'is-a' base that owns invariant state and template-method logic over that state; pick an interface for a capability/role that diverse types share or compose. With multiple interfaces you also get diamond resolution via super<T>. A common modern pattern is interface for the contract plus an Abstract...Base class that implements common parts.

go deeper

for a junior

Knows abstract class = single inheritance, interface = multiple, but may miss the state/constructor distinction.

for a middle

Identifies backing fields, constructors, and single-vs-multiple inheritance as the deciding factors.

for a senior

Gives a clear decision framework and the interface-plus-AbstractBase hybrid, mentions diamond resolution via super<T>.

for a principal

Reasons about API evolution (interfaces easier to add to via defaults vs abstract base owning invariants) and composition-over-inheritance trade-offs.

## The overlap Since Kotlin interfaces support **default implementations** (method bodies) and abstract members, the line is blurrier than in old Java. Both can mix abstract and implemented members. The decision hinges on what *only an abstract class* can do. ## What abstract classes have that interfaces lack - **Stored state with backing fields.** An abstract class property like `val cache = HashMap<...>()` has a real backing field. An interface property has **no backing field**: it must be abstract or computed through a getter, so an interface cannot store per-instance mutable state directly. - **Constructors.** Abstract classes have primary/secondary constructors, parameters, and `init` blocks, controlling initialization order. Interfaces have no constructor. - **Single inheritance.** A class extends at most one (abstract) class but can implement many interfaces. ```kotlin abstract class Connection(val url: String) { // constructor + state private var openCount = 0 // backing field, real state fun open() { openCount++; doOpen() } // template method over state protected abstract fun doOpen() } ``` ## What interfaces have that abstract classes lack - **Multiple inheritance of behavior (mix-ins).** A type can implement `Comparable`, `Closeable`, and a custom `Logged` interface at once. - **Diamond resolution.** When two interfaces provide the same default method, the implementer must override and disambiguate with `super<Type>.method()`. ## Decision guide - Need shared **mutable/initialized state** or a **constructor**? → abstract class. - Modeling a **capability/role** many unrelated types share or **compose**? → interface. - Want a default *and* the freedom to mix in other behaviors? → interface with defaults. - Common hybrid: a public **interface** (the contract) plus an **AbstractXxx** base class implementing the boilerplate, so callers depend on the interface while implementers extend the base. ## Other differences - Interface members are implicitly `open`/abstract; abstract-class members are final-by-default unless `open`/`abstract`. - Abstract-class visibility is full (`private`, `protected`); interface members can't be `protected` or hold private state.

  • Can an interface property hold mutable per-instance state?
    No — interface properties have no backing field, so they must be abstract or computed via a getter; only an implementing class (or abstract class) can give them real storage.
  • How is the diamond problem resolved when two interfaces both provide `fun f()`?
    The implementing class must override `f()` and explicitly call the chosen parent(s) with `super<InterfaceA>.f()` / `super<InterfaceB>.f()`.

An abstract class is a partially built engine you bolt your car onto (one chassis, shared parts); interfaces are plug-in capabilities you can snap onto many different machines.

saying these in an interview costs you the question

  • Saying interfaces can store mutable backing-field state
  • Claiming a class can extend multiple abstract classes
  • Ignoring constructors/init order as a deciding factor
  • Asserting abstract classes are always better than interfaces
  • Not knowing interface members are implicitly open

context