Kotlin Inheritance

Learn via video courses
Topics Covered

Overview

Inheritance in Kotlin is a fundamental OOP concept that allows the child class to inherit the properties and functions of the parent along with defining its specialized features. It enables us to create a hierarchical relationship between classes. It improves code reusability, abstraction, and polymorphism. However, careful design and consideration are essential to avoid complexities.

Syntax of Inheritance

In Kotlin by default, the classes are final and thus cannot be inherited. To make a class inheritable, we are required to declare it open by using the open keyword in front of the parent class. The inherited class is written after a colon (:), in the child class declaration.

  • Subclass - The class that inherits from another class. It is also called the child class.
  • Superclass - The parent class that can be inherited.

The syntax for the parent class is as follows:

The syntax for the child class is as follows:

Kotlin Inheriting property and functions from the base class

Let us look at an example to understand how a subclass has access to properties and functions of the base (parent) class.

Here, the base class is Vehicle and the child class is Car. The base class has two functions startEngine() and drive(). The child class also has two functions openTrunck() and drive().

Code:

Output:

Explanation: The drive() method is overridden by the child class to customize it. The openTrunck() method is the child class method and thus only available in the Car class. As the child class has access to all the methods and properties of the parent class we can call the method startEngine() from the Car class' object.

Use of Inheritance

As in the above of Vehicle class and Car class inheritance in Kotlin is used for given important use cases:

  • The common properties of all the vehicles such as starting the engine, driving, etc. can be stored in the parent class itself. Thus, the code efficiently is reused in all the child classes.
  • The properties that are common to all are present in one class and the properties specific to a particular class are defined in the class itself. Thus, inheritance promotes generalization and specialization.
  • For the common properties, if any modifications are made then these are consistent for all the child classes. Thus, it helps consistency and simplicity in maintaining the code.
  • If we require to specialize the general property for a specific class then we can always override.

Kotlin program of inheritance

Let us write a Kotlin program for Electronic devices. So we will define a parent class as ElectronicDevice using open keyword as follows, and it will take one parameter brand and will have two functions powerOn() and use() which is common to all electronic devices.

Now we can derive child classes of Electronic devices. Let us create a child class SmartPhone and Laptop. A smartphone can make a call so it will have a unique feature makeCall() and laptops can browse so we will have a feature openBrowser() in the Laptop class. We will override the customized use() function in both these classes. Code

Main function

We have created two objects SmartPhone and Laptop with their brand and model respectively.

Output:

Explanation: Unique features of the Laptop class are not available to smartphones such as openBrowser() and vice versa. It is also not available to the parent class, in our case openBrowser() cannot be called by ElectronicDevices. We can call and override all the methods and properties of the parent class.

Kotlin inheritance primary constructor

The primary constructor is a special constructor of a class in Kotlin that is defined directly within the header of the class. It initializes the properties of the class automatically when an object of the class is created.

When a derived class i.e. child class has a primary constructor, it must initialize the primary constructor of the base class or the parent class using the parameters passed to it.

This can be done in Kotlin by calling the constructor of the base class explicitly: Code:

Output:

Explanation: Here, we are explicitly calling the base class constructor Person(name, age) after the primary constructor using (:). We have two parameters in the Person class, additionally child class also has a parameter school.

When an instance of a child class is created using its primary constructor, the following steps are taken:

  • The parameters of the child class are initialized.
  • The super() keyword is used to call the primary constructor of the parent class. This will initialize the parameters of the parent class and execute its constructor.
  • The constructor body of the child class is executed.

Kotlin inheritance secondary constructor

Secondary constructors provide alternative ways to create instances of a class. These are defined using the constructor keyword. Some classes might only have secondary constructors.

All the constructors primary and secondary of the base class must be called by the subclass. This can be done using the super keyword in the secondary constructor of the subclass.

Additionally, a subclass can have its constructors as well. Code:

Output:

Explanation: Here, the parent class Vehicle has a primary constructor that takes a brand name as a parameter, and a secondary constructor that accepts brand and model names and calls its primary constructor having only a brand parameter.

The child class Car inherits Vehicle and has only a secondary constructor with parameters brand, model, and year that calls the secondary constructor of the Vehicle class.

Order of Execution for init Blocks, Primary and Secondary Constructors

Koltin allows any number of initializers and these are executed in the same order as defined in the code. init blocks or initializers are executed as a part of class construction.

Constructors are called based on the number and type of parameters passed to an instance. One constructor can call another constructor in its body.

To understand the order of execution of init blocks, primary constructors and secondary constructors of child and parent class let us take an example, execute it, and understand the output.

Code:

Output:

Explanation:

The order of execution, when an instance of a child class is created, is as follows:

  • The constructor of the child class is called, this calls the constructor of the parent class.
  • The constructor of the parent class executes initializers and initializes properties in the same order as written in the code.
  • Then the primary constructor of the parent class is executed and the control is passed to the child class.
  • The class now executes the initializers and initializes properties.
  • Lastly the primary constructor is executed by the child class.

instance of a child class

Overriding Member functions and properties

Overriding in OOP allows a subclass to provide specific implementation to a function or property already defined in the parent class. The open keyword must be used before the method or property to mark it as open for extension. The override keyword is used to explicitly tell the compiler that we are implementing the behavior of a method or a property.

Kotlin program of overriding the member function

Let us look at an example where we override a member function of a parent class.

Code:

Output:

Explanation: Here, the method draw() is overriden. It is marked open in the shape class and override is used to have a method with the same name as the parent class but a different implementation.

Kotlin program of overriding the member property

Code:

Output:

Explanation: The string property description of the Shape class is overridden and modified for the Circle class.

Calling the superclass implementation

We can use the members of the superclass from the derived class using the super keyword.

Let us take an example of a class Shape that has a property pi and a function calculateArea().

Code:

Output:

Explanation: Here, the value of PI is defined in the parent class and is used in all the child classes using the super keyword. The calculateArea() method is also overridden by both the subclasses Circle and Cylinder.

Advantages of using inheritance in Kotlin

The advantages of using inheritance in Kotlin are as follows:

  • Code Reuse: Inheritance in Kotlin allows us to write code once and then use it multiple times which saves a lot of time and effort.
  • Abstraction: Inheritance in Koltin promotes abstraction, we can represent ideas and concepts without worrying about the details of implementation.
  • Polymorphism: Inheritance in Kotlin makes code concise and easier to read by treating the different objects of the subclasses uniformly.

Disadvantages of using inheritance in Kotlin

The disadvantages of using inheritance in Kotlin are as follows:

  • Code coupling: Inheritance may create tight relations between classes and it may lead to difficulty in changing the code.
  • Hierarchies can become complex and confusing: If there are lots of hierarchies then it can become complex to keep track of relations between classes.
  • Can lead to God Classes: If the parent class has a lot many functionalities that are inherited in the child class then we may end up with a God class which is too large and complex to handle.

Conclusion

  • The OOPs concept of Inheritance in Kotlin empowers a child class to inherit attributes and functions from their parent class.
  • The open keyword marks a class, attribute, or function as extensible in the child class, allowing the child class to inherit, override, or extend its behavior and properties.
  • The override keyword is used to tell the compiler that the child class is providing a custom implementation for a function or property that is already defined in the superclass.
  • The order of execution when an instance of the child class instance involves the execution of initializers of the parent class, constructor of the parent class, initializers of the child class, and constructor of the child class.
  • Advantages of inheritance in Kotlin include code reuse and abstraction, while disadvantages encompass increased code coupling and potential complexity in class hierarchies.