Created By: Geoffrey Challen
/ Updated: 2022-09-26

Next we begin our exploration of relationships between Kotlin classes. So far the classes that we've created have stood alone—or at least we thought. In this lesson we'll see that all Kotlin classes are related to each other, and how to utilize those connections to reflect real-world relationships and improve our code.

Debugging Challenge

But let's start with some debugging practice!

First, A Puzzle

Let's begin this lesson with a puzzle:

If we compile and run this code, we see something printed. Which is... weird! Right? The snippet above uses dot notation to call a method on chuchu called toString. But where is that instance method defined? Do you see it? I don't! So why does this code work? On some level, this lesson is about figuring that out.


Kotlin allows us to establish relationships between classes. Specifically, Kotlin allows one class to inherit state and behavior by extending another class. Let's look at an example of this:

Inheritance Terminology and Syntax

We use the : notation to create a relationship between two classes. In the example above, we say that Student extends Person.

Note also that the parent must be marked as open. This allows it to be extended. If we omit this keyword, attempts to extend the class will fail:

This relationship is one way. The terminology that we use here is helpful. We refer to the class that is extended as the parent and the class that extends as the child:

This helps us remember that we cannot create circular class relationships. This won't compile:

We can also establish multiple levels of inheritance. When we do, we use similar family-based terminology:

Parent Class Initialization

When we extend a class, we need to make sure that our parent class is set up properly when instances of our class are created. For example, consider the hierarchy below. Whenever an instance of Student is created, we are also creating an instance of Person. So we need to make sure that the Person constructor gets called. Kotlin forces us to do this correctly. Let's see how:

Demonstrate how : notation forces us to call the parent constructor. Also show how to use super in a secondary constructor.


As a final observation, note that private still works the way that we expect. A class that extends another does not gain access to its private variables:

Show that private still works even under inheritance.


However, none of this really resolves our puzzle. We still don't know why this works:

Pet doesn't extend anything. So where is toString coming from?

To fill in the missing piece of the puzzle, we need to meet the class that sits at the top of Kotlin's class hierarchy, Any:

Go over the Any documentation. Identify the three most important Any methods: equals, toString, and hashCode.

Show how to complete the homework problem above. Feel free to cover multiple approaches!

Overriding Inherited Methods

So it's nice and all that every class will inherit a toString method from Any. But this method really isn't very useful!

For example, given that my Pet has a String name, I might want to display that instead. Can we do this? Yes! Let's look at how:

Show how to override toString. Demonstrate how in Kotlin you must mark the method using override to help identify errors.

We'll get into this more tomorrow and review exactly how Kotlin locates various method and field names when it compiles your code.

Show how to complete the homework problem above. Feel free to cover multiple approaches!

Show how to complete the homework problem above. Feel free to cover multiple approaches!

More Practice

Need more practice? Head over to the practice page.