Kotlin Inheritance

Filed Under: Kotlin
kotlin-inheritance

Continuing with our series of Kotlin tutorials, today we’ll look into inheritance in Kotlin Classes. Let’s get started by creating a new IntelliJ IDEA Kotlin project.

Kotlin Inheritance

Inheritance is the concept of creating class hierarchies wherein we override properties and functions of the base class in its subclasses as per our needs.

All classes in Kotlin have a common superclass : Any.

Inheritance allows code reusability. Typically the superclass is known as the base class or the parent class and the subclasses are the derived/child class.

Kotlin doesn’t allow a class to inherit from multiple classes because of famous diamond problem.

Kotlin Inheritance Example

Let’s put the above inheritance concept in our example code below.

We’ve created a base class as shown below:


open class Employee(eName: String) {
    init {
        println("He/She is an employee")
    }

    open var name = eName
    var age: Int = 24
    open fun printDetails() {
        println("Employee Name is $name. No specific details are available in this function.")
    }
}

By default all classes in Kotlin are final. So to allow a class to be inherited, we need to attach the open modifier before the class to make it non-final.

Also, to allow Properties and Functions to be overridden we need to set the open modifier on them too.

The init() function is executed as soon as an instance of the class is created.

Following code contains a derived class that extends the above class.


open class Developer(dName: String, income: Int) : Employee(dName) {
    init {
        println("He/She is an a Developer")
    }

    override var name = dName
    open var salary = income
    
    override fun printDetails() {
        println("Dev name is $name and salary is $salary")
    }
}
  • To inherit from class, we need to set the base class signature after :.
  • If the derived class(Developer) has a primary constructor, we need to initialize the base class(Employee) constructor right there using the parameters of the derived class.
  • If the derived class doesn’t have a primary constructor, you need to call the base class constructor in the secondary constructor using the keyword super.
  • To override a property or a function from the base class which has exactly the same type, you’ll need to append them with override to prevent compilation errors.
  • You cannot declare a function/property in a derived class with the same signature if it is NOT open in the base class and/or you haven’t set the override modifier to it.

Let’s run the above codes in our main function in the Kotlin class to see how the program flow.


fun main(args: Array<String>) {

    var employee = Employee("Anupam")
    employee.printDetails()

    println()

    var developer = Developer("Jack", 10000)
    developer.printDetails()
    println(developer.age) //gets the age property from the superclass
    
}

The output should look like this:

kotlin inheritance example

  • When an instance of the developer class is created, it first calls the init block of the superclass followed by its own.
  • The subclass can access all non-private properties and functions of its superclass.
  • The superclass instance can be set to the subclass object. Doing so, the superclass can only access properties and functions in the subclass that are overridden.

The below code showcases setting the superclass instance to the subclass.


employee = developer
employee.printDetails() // Dev name is Jack and salary is 10000
println(employee.name) //Prints Jack

As it is evident in the above code, the superclass prints the overridden property and function.

Let’s created subclasses of the Developer class now.



class AndroidDeveloper(name: String, income: Int) : Developer(name, income) {

    override var name = "aName property value is $name".also(::println)
    override var salary: Int = 0
        get() = field
        set(value) {
            if (value >= 100000) {
                field = min(50000, value)
            } else field = value
        }

    init {
        println("He/She is an Android Developer. If Salary >= 100000. It's halved.")
        salary = income
    }

    fun works() {
        println("Builds Apps")
    }

    override fun printDetails() {
        super.printDetails()
        println("He's an Android Developer")


    }
}

class JuniorDeveloper : Developer {
    var aName = "Name is $name".also(::println)

    init {
        println("He/She is a Junior Developer.")
    }

    var mySkill: String?

    override var salary: Int = 0
        get() = field
        set(value) {
            if (value > 50000) {
                field = min(10000, value)
            } else field = value
        }


    constructor(name: String, income: Int, skill: String) : super(name, income) {
        mySkill = skill
        salary = income
    }

    override fun printDetails() {
        println("Junior dev name is $name and salary is $salary, Skill is $mySkill")
    }
}

The JuniorDeveloper class doesn’t have a primary constructor, so we call the superclass class in the secondary constructor using super.

In both of the subclasses above we’ve set custom getters and setters on the overridden property salary.
The setter property doesn’t get invoked when the property value is assigned for the first time in the definition itself.

Let’s run the class instances in the main function again.


    val androidDeveloper = AndroidDeveloper("Rose", 100000)
    androidDeveloper.printDetails()
    androidDeveloper.salary = 100000
    println("Updated Salary : ${androidDeveloper.salary}")

    println("\nJunior Developer class......\n")
    val juniorDeveloper = JuniorDeveloper("Brock", 60000, "Kotlin")
    juniorDeveloper.printDetails()
    juniorDeveloper.salary = 60000
    println("Junior Dev new salary is ${juniorDeveloper.salary}")
    println("Assigning juniordeveloper to super superclass\n")
    employee = Employee("Anupam")
    employee = juniorDeveloper
    print(employee.salary)

The following output is printed.
kotlin inheritance

So for either of the instance creations, ALL the init blocks of the superclasses get invoked first.
In the JuniorDeveloper class, we had to set the properties in the secondary constructor manually.

Hence, for the juniordeveloper instance, the property setter gets instantly invoked.

In the last segment of the code, we’d set the employee instance to the juniordeveloper instance to fetch the overridden property.

The next example uses Interfaces and classes together in the class hierarchy.


open class Manager {
    init {
        println("He/She is a manager.")
    }

    open lateinit var mName: String
    open var salary: Int? = null


    constructor(name: String) {
        mName = name
    }

    constructor(income: Int) {
        salary = income
    }

    open fun printDetails() {
        println("Name is $mName. Salary is $salary")
    }


}

interface X {
    fun printDetails() {
        print("He has an X-Factor")
    }
}

In the class, we’ve created two secondary constructors. The interface and the class have the same function signature. Let’s see how the subclasses deal with it.


class ProjectManager : Manager {
    var numberOfProjects: Int?
    var pName: String?


    init {
        println("He/She is a Project Manager.")
    }

    constructor(number: Int) : this("Ben", 10)

    constructor(name: String, number: Int) : super(name) {
        numberOfProjects = number
        pName = "Congrats ${super.mName}"
    }

    override fun printDetails() {
        super.printDetails()
        println("$pName He's handled $numberOfProjects projects so far")
    }

}


class BackendManager : Manager, X {

    init {
        println("He/She is a Backend Manager.")
    }

    var isBackendReady: Boolean?
    val backendLanguage: String?

    override var mName = "NA"

    constructor(name: String, language: String) : super(name) {
        println("Constructor 1 gets called.")
        isBackendReady = false
        backendLanguage = language
        mName = name
    }

    constructor(isReady: Boolean, name: String) : this("Anupam", "PHP") {
        println("Constructor 2 gets called.")
        isBackendReady = isReady

    }

    override fun printDetails() {
        super<Manager>.printDetails()
        println("The backend he's working on, is it ready? $isBackendReady.")
        super<X>.printDetails()
    }
}

In the class(ProjectManager), the first constructor delegates to the second constructor using this. So the second constructor would be executed before the first constructor.

super.mName is used to access the property from the superclass.

Since it is an Optional in this class we need to safely unwrap it using the ?. operator.

Since the function signature is the same in the superclass and interface, to make a correct call, we need to do super<Type>.printDetails().

The output when the instances of the above classes are created is given below.


    var manager = Manager("Thomas")
    manager.printDetails()

    println()

    manager = ProjectManager(number = 10, name = "Ben")
    manager.printDetails()

    println()

    val backendManager = BackendManager(true, "Smith")
    backendManager.printDetails()

kotlin inheritance with interfaces

In the BackendManager class, the Constructor 2 is invoked, which first executes constructor 1.

That’s all for Kotlin inheritance example tutorial.

Reference: Kotlin Docs

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages