Menu
Coddy logo textTech

Open Classes and Subclasses

Part of the Object Oriented Programming section of Coddy's Kotlin journey. Lesson 17 of 57.

Inheritance lets one class build on another. The subclass gets every property and method of its superclass and can add its own. Kotlin classes are closed by default, so a class has to be marked open before another class can extend it:

open class Animal(val name: String) {
    fun describe() = "$name is an animal"
}

class Dog(name: String) : Animal(name) {
    fun fetch() = "$name fetches the ball"
}

Inside main:

val rex = Dog("Rex")
println(rex.describe())
println(rex.fetch())

Output:

Rex is an animal
Rex fetches the ball

class Dog(name: String) : Animal(name) says that Dog extends Animal and passes name on to the superclass constructor. The parameter has no val, because the property already exists in Animal. A class that is not open cannot be extended:

class Plant(val name: String)
// class Tree(name: String) : Plant(name)   // error: this type is final, so it cannot be extended

open class Vehicle(val wheels: Int)
class Bike : Vehicle(2)                     // passes a fixed value
class Truck(val load: Int) : Vehicle(6)     // adds its own property

A Dog is an Animal, so a subclass instance can be used wherever the superclass type is expected. One List<Animal> can hold dogs, cats and plain animals, and every element has the members of Animal:

open class Animal(val name: String) {
    fun describe() = "$name is an animal"
}
class Dog(name: String) : Animal(name)
class Cat(name: String, val indoor: Boolean) : Animal(name)

Inside main:

val pets: List<Animal> = listOf(Dog("Rex"), Cat("Tom", true), Animal("Generic"))
for (p in pets) println(p.describe())

Output:

Rex is an animal
Tom is an animal
Generic is an animal

Inheritance models an is-a relation: a dog is an animal, a savings account is an account. When one class only uses another (a car has an engine), give it a property of that type instead. Closed classes are Kotlin's default so that a class meant for extension says so with open, and every other class keeps its behaviour safe from subclasses.

open class Account(val owner: String)
class SavingsAccount(owner: String) : Account(owner)   // is-a: inheritance

class Engine(val horsepower: Int)
class Car(val engine: Engine)                          // has-a: a property
challenge icon

Challenge

Easy

Manager repeats what Employee already has. Make Employee open and turn Manager into its subclass: Manager(name: String, salary: Int) passes both values to Employee and keeps only what is new, a private list of report names with addReport(name) and teamSize(). In Employee, raise(percent) increases the salary by that percentage, rounded down, and describe() returns Ada earns 5500.

The supplied code creates a manager from the first line (name,salary), adds each following name as a report, gives a 10% raise and prints Ada earns 5500 and leads 2, then checks that a manager is an Employee.

Your code goes in Employee.kt and Manager.kt. Main.kt holds the supplied input/output code and cannot be edited.

Try it yourself

fun main() {
    // Supplied input/output code: keep it as it is
    val input = generateSequence(::readLine).toList()
    val (name, salary) = input[0].split(",")
    val boss = Manager(name, salary.toInt())
    for (report in input.drop(1)) boss.addReport(report)
    boss.raise(10)
    println("${boss.describe()} and leads ${boss.teamSize()}")
    println("a manager is an employee: ${(boss as Any) is Employee}")
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Kotlin playground