open, override and super
In Java every class can be extended unless you mark it final. Kotlin flips that: classes and members are final by default. To allow a subclass you write open class, and each function or property a subclass may replace must also be open. The subclass marks its replacement with override — a required keyword, so you can never override by accident or fail to override because of a typo.
open class Notifier(val channel: String) {
open fun format(msg: String) = "[$channel] $msg"
fun send(msg: String) = println(format(msg)) // final: not overridable
}
class UrgentNotifier : Notifier("pager") {
override fun format(msg: String) = super.format(msg.uppercase()) + " !!!"
}
fun main() {
Notifier("email").send("build passed")
UrgentNotifier().send("disk full")
}[email] build passed
[pager] DISK FULL !!!super.format(...) reuses the parent's version instead of copying it.
Add an open val prefix = "" to Notifier, use it in format, and override it in the subclass as override val prefix = "URGENT ".
The subclass header : Notifier("pager") both names the parent and calls its constructor — the parent is always fully built before the child's own initialisers run. An override is itself open to further subclasses; write final override to stop the chain.
Extending a class that is not open
class Animal(val name: String)
class Dog(name: String) : Animal(name)
fun main() = println(Dog("Rex").name)Main.kt:2:27: error: this type is final, so it cannot be extended.
class Dog(name: String) : Animal(name)
^^^^^^Kotlin classes are final unless marked open. The designers chose this because a class that was never designed for extension breaks in subtle ways when someone subclasses it (the "fragile base class" problem).
Add open to the parent if it really is designed to be extended. Often the better answer is an interface or composition instead of inheritance.
open class Animal(val name: String)
class Dog(name: String) : Animal(name)
fun main() = println(Dog("Rex").name)Overriding a member that is not open
open class Base {
fun greet() = "hi"
}
class Child : Base() {
override fun greet() = "hello"
}
fun main() = println(Child().greet())Main.kt:5:5: error: 'greet' in 'Base' is final and cannot be overridden.
override fun greet() = "hello"
^^^^^^^^Opening the class does not open its members. Each function and property is final until it is marked open (or abstract) on its own.
Mark the function open in the parent.
open class Base {
open fun greet() = "hi"
}
class Child : Base() {
override fun greet() = "hello"
}
fun main() = println(Child().greet())init calls an open function the child overrides, the child's version runs before the child's own properties are initialised — and a non-null String property can be read as null. The example below shows it happening.open class Base {
init { println("Base sees: ${describe()}") }
open fun describe() = "base"
}
class Child : Base() {
private val name: String = "child"
override fun describe() = "name=$name"
}
fun main() {
val c = Child()
println("After construction: ${c.describe()}")
}Base sees: name=null
After construction: name=childkotlinc compiles this without complaint; IntelliJ flags it with an inspection. Treat that highlight as an error.
