Start learning free

LESSON 186 OF 348 · OBJECT-ORIENTED PROGRAMMING

Inheritance in Kotlin: open Classes and Subclasses

Lesson Read the full explanation

TL ; DR:

A child class can inherit from a parent class — it gets all the parent's fields and methods for free, and can add its own. This lets you reuse code instead of copy-pasting it.


class Puppy(name: String) : Dog(name) {
    fun whine() { println("whimper") }
}


Full lesson:

Inheritance is one of the four classic OOP pillars (along with encapsulation, polymorphism, and abstraction). The idea is simple: most real-world things are kinds of other things — a Puppy is a Dog, a Car is a Vehicle, a Manager is an Employee. When you model that "is-a" relationship in code, the child class gets the parent's data and behavior automatically.

What gets inherited?

  • Fields — the Puppy has a name field even though we only declared it on Dog.
  • Methods — the Puppy can call bark() even though we only defined it on Dog.
  • The contract — anywhere code expects a Dog, you can pass a Puppy (we'll see this when we cover polymorphism).

What you still write

  • The constructor usually has to forward arguments up to the parent's constructor (because the parent's fields need initializing).
  • Any new methods or new fields specific to the child.

Your task

Below the Dog class, create a Puppy class that inherits from Dog and adds one new method, whine(), that prints "whimper". The harness creates a Puppy named "Buddy", calls its inherited bark(), and then its new whine().

Expected output:

Woof! I am Buddy
whimper
STEP 1 OF 2
    Ctrl + Enter to check

    KOTLIN · OBJECT-ORIENTED PROGRAMMING

    Inheritance in Kotlin, explained

    Is-a relationships

    Many things are kinds of other things: a puppy is a dog, a car is a vehicle. Inheritance models that "is-a" relationship in code. A child class (subclass) gets the parent's (superclass's) properties and functions automatically, and adds its own. Instead of copying the same code into two classes, you write it once.

    Kotlin classes are closed by default

    In Kotlin you cannot extend just any class. A class is final unless you mark it open. This protects classes that were not designed to be extended:

    open class Dog(val name: String) {
        fun bark() {
            println("Woof! I am $name")
        }
    }
    

    Without open, the line class Puppy(name: String) : Dog(name) fails with this type is final, so it cannot be extended. The lesson's starter code already has the open.

    Declaring a subclass

    A colon after the class header names the parent, and the parent's constructor is called right there:

    class Puppy(name: String) : Dog(name) {
        fun whine() {
            println("whimper")
        }
    }
    

    The part : Dog(name) does two jobs. It says that Puppy inherits from Dog, and it passes the name argument to Dog's constructor. A parent's properties must be initialized, and the parent's constructor is the code that does it. If you write only : Dog, the compiler reports that the type has a constructor and must be initialized.

    Notice that the parameter in Puppy(name: String) has no val. It is a plain constructor argument that is forwarded. Writing val name again in Puppy would clash with the inherited property, and the compiler would say that name hides a member of the supertype.

    What a subclass gets

    • Properties. A Puppy has name, declared only in Dog.
    • Functions. A Puppy can call bark(), declared only in Dog.
    • The type. A Puppy can be used wherever a Dog is expected: val d: Dog = Puppy("Buddy") compiles.

    The reverse is not true. Through a variable of type Dog you cannot call whine(), even when the object is a Puppy, because the compiler only knows about what Dog offers. The error is unresolved reference 'whine'.

    A subclass can also take extra constructor arguments of its own, passing only some of them on:

    class Puppy(name: String, val ageMonths: Int) : Dog(name) {
        fun info() = "$name is $ageMonths months old"
    }
    

    Puppy("Buddy", 3).info() returns Buddy is 3 months old.

    One parent, many children

    A class can have only one direct parent class, but a parent can have many children. Puppy and, say, Hound can both extend Dog, and each gets bark() without repeating it. A change to bark() in Dog then reaches all of them. Every Kotlin class also has the root type Any above it, which is why every object already has functions such as toString().

    Construction order

    When you create a subclass object, the parent part is built first. If both classes have init blocks, the parent's runs before the child's. For a Dog that extends Animal and prints from each init, creating one dog prints Animal Rex created and then Dog created. This is why the parent constructor call is part of the subclass header: the parent's properties must be ready before the child's code starts.

    The exercise

    The starter code has the open Dog and a mainFunction that creates a Puppy named "Buddy", calls bark() and then whine(). Step 1 declares Puppy with : Dog(name). Step 2 adds whine, since bark is inherited:

    open class Dog(val name: String) {
        fun bark() {
            println("Woof! I am $name")
        }
    }
    
    class Puppy(name: String) : Dog(name) {
        fun whine() {
            println("whimper")
        }
    }
    
    class Solution {
        fun mainFunction() {
            val buddy = Puppy("Buddy")
            buddy.bark()
            buddy.whine()
        }
    }
    

    The output is:

    Woof! I am Buddy
    whimper
    

    Common mistakes

    • Forgetting open on the parent. The subclass will not compile.
    • Omitting the parent's constructor call. Write Dog(name), with parentheses and arguments.
    • Adding val to the forwarded parameter. That redeclares the inherited property.
    • Redefining bark() in Puppy to "reuse" it. It is inherited already. Changing what it does is the next lesson, overriding.
    • Inheriting when "has-a" fits. A dog has a name, but is not a kind of name. Use inheritance only for true is-a relationships.

    Key takeaways

    • A subclass inherits properties and functions from its parent and can add more.
    • Parents must be marked open; classes are final by default.
    • : Dog(name) declares inheritance and calls the parent's constructor.
    • A Puppy can be used as a Dog, but through a Dog variable only Dog's members are visible.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free