ref – https://www.programiz.com/kotlin-programming/inheritance
Why do we inherit?
Suppose, in your application, you want three characters – a math teacher, a footballer and a businessman.
Since, all of the characters are persons, they can walk and talk. However, they also have some special skills. A math teacher can teach math, a footballer can play football and a businessman can run a business.
In each of the classes, you would be copying the same code for walk and talk for each character. This means you’d be implementing walk and talk three different times for our three characters.
And to make matters worse, if you want to add a new feature (i.e eat) you need to implement the eat three different times (once for each character!). This can easily become error prone (when copying) and duplicate codes.
It would be a lot easier if we had a Person class with basic features like talk, walk, eat, sleep, and add special skills to those features as per our characters. This is done using inheritance.

Using inheritance, now we don’t need to repeatedly implement the same code for walk(), talk() and eat() for each class. We just implement a class with all these common features. Then when we create each charaeter, we simlpy need to inherit from our common class.
So, for MathTeacher (derived class), you inherit all features of a Person (base class) and add a new feature teachMath(). Likewise, for the Footballer class, you inherit all the features of the Person class and add a new feature playFootball() and so on.
This makes your code cleaner, understandable and extendable.
Is A
It is important to remember: When working with inheritance, each derived class should satisfy the condition whether it is a base class or not.
In the example above, MathTeacher is a Person, Footballer is a Person.
You cannot have something like, Businessman is a Business.
Make it inheritable by using Open
By default, classes in Kotlin are final. A final class cannot be subclassed. By using the open annotation on a class, compiler allows you to derive new classes from it.
Hence, if we want to inherit from say class A, we MUST put open in front of class A.
We have a primary constructor
|
open class Person(age: Int, name: String) { init { println("My name is $name.") println("My age is $age") } } |
We then declare our characters. In our primary constructor, we extend from class Person.
We then pass in our primary constructor’s parameters age and name into our parent class Person.
|
class MathTeacher(age: Int, name: String): Person(age, name) { fun teachMaths() { println("I teach in primary school.") } } |
|
class Footballer(age: Int, name: String): Person(age, name) { fun playFootball() { println("I play for LA Galaxy.") } } fun main(args: Array<String>) { val t1 = MathTeacher(25, "Jack") t1.teachMaths() println() val f1 = Footballer(29, "Christiano") f1.playFootball() } |
Child primary constructor must use base
If the derived class has a primary constructor, the base class must be initialized using the parameters of derived class’s primary constructor.
The base class must be executed first. Then, the derived primary constructor executes.
In the above program, both derived classes have two parameters age and name, and both these parameters are initialized in primary constructor in the base class.
In our next example, we have base class Person. We want it to be the base class where other classes can derive from it so we use open.
We then give Person a constructor.
|
open class Person { var age: Int = 0 var name: String = "" constructor(newAge: Int, newName: String) { println("Person constructor (newAge, newName)") age = newAge name = newName } } |
We then create Footballer which extends Person. In order for this extending to take place, we must put the base class behind our Footballer class like so:
|
class Footballer(age: Int, name: String, club: String): Person(age, name) {...} |
This means Person constructor gets executed first.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
open class Person { var age: Int = 0 var name: String = "" constructor(newAge: Int, newName: String) { println("Person constructor (newAge, newName)") age = newAge name = newName } } // primary constructor // calls Parent class constructor class Footballer(age: Int, name: String, club: String): Person(age, name) { init { println("Football player $name of age $age and plays for $club.") } fun playFootball() { println("I am playing football.") } } fun main(args: Array<String>) { val f1 = Footballer(29, "Cristiano", "LA Galaxy") } |
output:
Person constructor (newAge, newName)
Football player Cristiano of age 29 and plays for LA Galaxy.
Child that has no primary constructor
In case of no primary constructor, each base class has to initialize the base (using super keyword), or delegate to another constructor which does that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
open class Log { var data: String = "" var numberOfData = 0 constructor(_data: String) { println("Log - 1st constructor - with parameter $_data") } constructor(_data: String, _numberOfData: Int) { data = _data numberOfData = _numberOfData println("Log - 2nd constructor - $data: $numberOfData times") } } class AuthLog: Log { // calls another of its own constructor that delegates to the base class constructor(_data: String): this("From AuthLog -> + $_data", 10) { println("AuthLog - first constructor with parameter $_data") } // calls parent class's constructor FIRST // after that, it will execute the below constructor which delegates to super constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) { println("AuthLog - second constructor with parameter $_data, $_numberOfData") } } fun main(args: Array<String>) { val p1 = AuthLog("Bad Password") } |