Inheritance and Interfaces
|
This section documents Kotlin 2.4.x on the JVM, as published at kotlinlang.org, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against kotlinlang.org before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Kotlin’s inheritance model is Java’s, with one inversion (explicit open, covered on
Classes and Objects) and one addition with no Java equivalent
(class delegation via by).
open and override
A base class — and each member a subclass should be able to override — must be marked open explicitly;
overriding itself must be marked override explicitly too, so accidentally overriding (or accidentally failing
to override) a base member is a compile error rather than a silent typo:
open class Shape(val name: String) {
open fun area(): Double = 0.0
fun describe() = "$name has area ${area()}" // NOT open -- cannot be overridden
}
class Circle(val radius: Double) : Shape("circle") {
override fun area(): Double = Math.PI * radius * radius
}
final override fun area() re-seals a member so no further subclass can override it again — the inheritance
equivalent of Java’s final on a method, but only meaningful once something is already open.
Abstract Classes
abstract class works as in Java: it cannot be instantiated directly, and its abstract members carry no body — and, notably, an abstract member is implicitly open (there would be no point requiring open on a member
with no default implementation to protect):
abstract class Animal {
abstract fun sound(): String // no "open" needed -- abstract members already are
fun describe() = "This animal says: ${sound()}"
}
class Dog : Animal() {
override fun sound() = "Woof"
}
Interfaces with Default and Property Members
A Kotlin interface can declare a method with a body (a default implementation, like Java’s default methods)
and can declare abstract properties — something Java interfaces cannot do at all:
interface Greetable {
val greeting: String // abstract property -- implementer must provide it
fun greet() = println(greeting) // default implementation, like Java's "default" methods
}
class English : Greetable {
override val greeting = "Hello!" // implemented as a property
}
class Loud : Greetable {
override val greeting get() = "HELLO!!" // implemented as a computed property
override fun greet() = println(greeting.repeat(2)) // overriding the default too
}
Unlike a class, an interface has no state of its own — an interface property must either be abstract (as above) or computed from something else; it cannot hold a backing field.
Class Delegation via by
Kotlin can implement an interface by delegating every member to another object, with one keyword and zero boilerplate forwarding methods — there is no equivalent one-liner in Java, where the same result requires hand-writing every forwarding method:
interface Repository {
fun findById(id: Int): String?
}
class InMemoryRepository : Repository {
private val data = mapOf(1 to "Ada", 2 to "Grace")
override fun findById(id: Int) = data[id]
}
// LoggingRepository implements Repository entirely by forwarding to "delegate" ...
class LoggingRepository(private val delegate: Repository) : Repository by delegate {
// ...except for findById, which is overridden to add logging around the delegated call
override fun findById(id: Int): String? {
println("looking up $id")
return delegate.findById(id)
}
}
This is the Decorator pattern with the forwarding boilerplate eliminated by the compiler — by delegate
generates an implementation of every interface member that simply calls the same member on delegate, and any
member declared explicitly (as findById is above) overrides that generated forwarding.
Kotlin’s Explicit open vs. Java’s Implicit-Open Default
any non-final class is open by default class KotlinShape { <
explicitly open note for KotlinFinalShape "final by default —
subclassing requires open"
See Also
-
Classes and Objects — why classes are
finalby default. -
Sealed Classes and Enums — a closed, exhaustively matchable subtype hierarchy.
-
Java or Kotlin for Spring Boot? — why Spring needs the
kotlin-allopenplugin.