Skip to content

Beginner: Kotlin · Classes, objects and collections · topic 11 of 50 · 25 min

Inheritance and interfaces

Explanation

Inheritance lets a class reuse and extend another class. In Kotlin, classes are final (closed) by default: you must mark a class open to inherit from it and a function open to override it. Abstract classes leave some members for subclasses to fill in. Interfaces describe abilities that many unrelated classes can share, and a class can implement several interfaces.

Why inheritance?

Sometimes several kinds of things share a lot: a bus ticket, a train ticket and a flight ticket all have a passenger name and a price, but each calculates its fee differently. Inheritance lets a subclass (child) reuse the properties and functions of a superclass (parent) and change only what differs.

open and override

Kotlin chose a safe default: classes and their functions are final, meaning they cannot be inherited or overridden, unless you say open. This prevents accidental changes in subclasses that the original author never planned for.

kotlin
open class Ticket(val passenger: String, val baseFare: Int) {
    open fun total(): Int = baseFare
    fun summary() = "$passenger: Rs ${total()}"
}

class TrainTicket(passenger: String, baseFare: Int, val tatkal: Boolean) : Ticket(passenger, baseFare) {
    override fun total(): Int = if (tatkal) baseFare + 300 else baseFare
}

class FlightTicket(passenger: String, baseFare: Int) : Ticket(passenger, baseFare) {
    override fun total(): Int = baseFare + 750       // fees
}

fun main() {
    val tickets: List<Ticket> = listOf(TrainTicket("Ravi", 1200, true), FlightTicket("Meena", 4500))
    for (t in tickets) println(t.summary())
}
Prints Ravi: Rs 1500 and Meena: Rs 5250. summary calls the right total for each object (polymorphism).

Polymorphism (many forms) means one call, t.total(), does different things depending on the actual object. The loop does not need to know which kind of ticket it has.

The subclass header TrainTicket(...) : Ticket(passenger, baseFare) passes values up to the parent constructor. The parent's properties (passenger, baseFare) are then available in the child without being declared again.

super

kotlin
class FestiveTrainTicket(p: String, fare: Int) : Ticket(p, fare) {
    override fun total(): Int = super.total() - 100    // reuse the parent's logic, then adjust
}
super calls the parent class's version.

Abstract classes

An abstract class cannot be created directly; it exists to be inherited. It can declare abstract members with no body, which every subclass must implement, alongside normal shared code.

kotlin
abstract class Shape {
    abstract fun area(): Double
    fun describe() = "Area: %.2f".format(area())
}

class Circle(val r: Double) : Shape() {
    override fun area() = Math.PI * r * r
}
Shape() cannot be created; Circle(2.0) can.

Interfaces

An interface describes an ability, like "can be shared" or "can be paid". Many unrelated classes can implement it, and a class can implement several interfaces but extend only one class. Interface functions can have default bodies.

kotlin
interface Payable {
    fun amountPaise(): Long
    fun display() = "Rs ${amountPaise() / 100}"    // default implementation
}

interface Shareable {
    fun shareText(): String
}

class Invoice(val no: String, val paise: Long) : Payable, Shareable {
    override fun amountPaise() = paise
    override fun shareText() = "Invoice $no: ${display()}"
}
Invoice has both abilities.
Think of it like this: Inheritance is like a family business: the son's shop inherits the father's name, suppliers and way of working, and changes a few things. An interface is like a certification such as "accepts UPI": a medical store, a tea stall and a bookshop are unrelated, but each can display the same sticker and promise the same ability.

Composition over inheritance

Deep inheritance trees (A extends B extends C extends D) become hard to change. Most modern Kotlin and Android code prefers composition: a class holds other objects that do parts of the work, and interfaces describe what can be swapped. Use inheritance for a genuine "is a" relationship and keep trees shallow.

Common mistakes

  • Forgetting open: "This type is final, so it cannot be inherited from".
  • Forgetting override when redefining a function: the compiler requires it explicitly.
  • Trying to inherit from two classes. Use one class plus interfaces.
  • Building deep hierarchies for code reuse. Prefer small classes composed together.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Inheritance and interfaces · 1/6

Parent → Child

Inheritance lets a subclass reuse and change a parent class.

Prefer a video by a teacher? Find free videos on “kotlin inheritance open override abstract interface” on YouTube ↗

In real life: Notification channels

A banking app sends alerts by SMS, email and push notification. A Notifier interface with send(message) is implemented by SmsNotifier, EmailNotifier and PushNotifier. The alert code holds a List<Notifier> and calls send on each. Adding WhatsApp means one new class implementing Notifier, with no change to the alert code.

kotlin
interface Notifier { fun send(msg: String): String }

class SmsNotifier(val phone: String) : Notifier {
    override fun send(msg: String) = "SMS to $phone: $msg"
}
class EmailNotifier(val email: String) : Notifier {
    override fun send(msg: String) = "Email to $email: $msg"
}

fun main() {
    val channels: List<Notifier> = listOf(SmsNotifier("98xxxx10"), EmailNotifier("a@b.in"))
    channels.forEach { println(it.send("Rs 2,000 debited")) }
}
The loop works with any Notifier.

Practice (0/3 done)

Task 1 · Question

Why does this not compile? class Animal class Dog : Animal()

Task 2 · Question

What does this print? open class A { open fun hi() = "A" } class B : A() { override fun hi() = "B" + super.hi() } fun main() { val x: A = B(); println(x.hi()) }

Task 3 · Practice

Create an interface Discountable with fun discountPercent(): Int and a default fun priceAfter(price: Int) = price - price * discountPercent() / 100. Implement it in StudentCustomer (10%) and SeniorCustomer (15%), put both in a List<Discountable>, and print the price after discount for Rs 1,000.

Quick quiz

Question 1

1.Which keyword allows a class to be inherited?

Question 2

2.How many classes can a Kotlin class extend?

Question 3

3.What is polymorphism?

Question 4

4.Can an interface function have a default body in Kotlin?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Which keyword allows a class to be inherited?

Answer: open

Classes are final unless marked open (or abstract).

How many classes can a Kotlin class extend?

Answer: One

One superclass, but many interfaces.

What is polymorphism?

Answer: One call behaving differently depending on the actual object

The overridden function of the real object runs.

Can an interface function have a default body in Kotlin?

Answer: Yes

Interfaces can provide default implementations.

How many classes can a Dart class extend?Related

Answer: One

Single inheritance; many interfaces and mixins.

From Inheritance, interfaces, mixins and extensions

How many classes can a Java class directly extend?Related

Answer: One

Java has single inheritance for classes.

From Inheritance with extends

class B extends A {} (no constructor). What does new B(1, 2) do?Related

Answer: Calls A's constructor with 1, 2

A missing constructor in a subclass passes all arguments to super automatically.

From Inheritance and composition

A child class defines speak() and so does its parent. Which runs for child.speak()?Related

Answer: The child's version

Python looks for the method on the child first. This is overriding.

From Inheritance and composition

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Classes, objects and collections” before its exam: Collections: List, Set and Map, Collection operations.
  2. Module 3 exam: Classes, objects and collections · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Coroutines and Flow. 50 topics left in the course before the final exam.

← Previous: Data, enum and sealed classes, and objects