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.
Let’s begin the lesson with a puzzle:
If we compile and run this code, we see something printed.
Which is… weird!
The snippet above uses dot notation to call a method on
But where is that instance method defined?
Do you see it?
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:
We use the
: notation to create a relationship between two classes.
In the example above, we say that
Note also that the parent must be marked as
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:
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
So we need to make sure that the
Person constructor gets called.
Kotlin forces us to do this correctly.
Let’s see how:
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
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,
Create a class called
Student that inherits from a class called
(Do not create
Person. It is already available.)
Define a single
Student constructor that takes a
String? argument (name) and an
(university ID number).
You should call the
Person constructor and pass that
(You don't need to do anything else with it.)
You should also provide a public getter named
getID for the student ID.
Reject negative ID numbers using
So it’s nice and all that every class will inherit a
toString method from
But this method really isn’t very useful!
For example, given that my
Pet has a
name, I might want to display that instead.
Can we do this?
Let’s look at how:
We’ll get into this more tomorrow and review exactly how Kotlin locates various method and field names when it compiles your code.
ScoreTracker that we'll use to keep track of the scores of two players who are
playing a game.
Players playing this game always take turns, and Player 1 always plays first.
Both players scores start at zero.
ScoreTracker should provide a method
score that accepts a number of points and does not return a value.
If it is Player 1's turn, the points (which might be negative) get added to their score.
Otherwise they get added to Player 2's score.
You should also provide a method
currentlyAhead that returns
1 if Player 1 is ahead,
2 if Player 2 is ahead,
0 if it's a tie.
You should not expose any of your state publicly, and
ScoreTracker does not need to provide a public constructor.
When your class is complete, here is how it should work:
Need more practice? Head over to the practice page.