Inheritance & Interfaces
Classes are final by default
To allow subclassing, mark a class open. Members you want to override must also
be open:
open class Animal(val name: String) {
open fun sound(): String = "..."
}
class Dog(name: String) : Animal(name) {
override fun sound(): String = "Woof"
}
: Animal(name)calls the superclass constructor.overrideis required (not optional) when redefining a member.
Call the parent implementation with super:
class LoudDog(name: String) : Animal(name) {
override fun sound(): String = super.sound() + "!!!"
}
Abstract classes
An abstract class cannot be instantiated and may leave members unimplemented:
abstract class Shape {
abstract fun area(): Double // no body — subclasses must implement
fun describe() = "Area is ${area()}" // concrete
}
Interfaces
Interfaces declare behavior and can include default implementations, but hold no state:
interface Drawable {
fun draw() // abstract
fun show() = println("drawing") // default implementation
}
interface Clickable {
fun onClick()
}
class Button : Drawable, Clickable { // implement multiple interfaces
override fun draw() = println("[ Button ]")
override fun onClick() = println("clicked")
}
When two interfaces provide the same default method, disambiguate with
super<Type>:
interface A { fun hi() = "A" }
interface B { fun hi() = "B" }
class C : A, B {
override fun hi() = super<A>.hi() + super<B>.hi()
}
Class vs interface — which?
- Use an interface for a capability many unrelated types can have
(
Drawable,Comparable). - Use an abstract class when subtypes share state or constructor logic.
Exercises
-
Create an
open class Vehiclewith anopen fun describe()and subclassesCarandBicyclethat override it. -
Define an interface
Greeterwith a defaultgreet()that returns"Hello", and a class that overrides it to return"Hi".
Previous: Visibility & Packages · Next: Data, Enum & Sealed Classes