Kotlin – 14 – Inheritance in Kotlin

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows you to create new classes based on existing classes, thereby promoting code reuse and the organization of related classes in a hierarchy. Kotlin supports inheritance, and in this guide, we’ll explore how to use it effectively in Kotlin, including the syntax, overriding methods, and accessing superclass members.

Defining a Superclass

In Kotlin, you define a superclass using the class keyword, just like you would for any other class. A superclass typically contains properties and methods that are common to its subclasses. Here’s a basic example of a superclass called Shape:

open class Shape {
    open fun area(): Double {
        return 0.0
    }
}

In this example, Shape is a simple class with a method area(), which calculates the area of a shape. The open keyword before the class declaration and the open keyword before the area() method indicate that they can be overridden by subclasses.

Creating a Subclass

To create a subclass in Kotlin, you use the : symbol followed by the name of the superclass in the class header. Subclasses inherit properties and methods from their superclass. Here’s an example of a Circle subclass that inherits from the Shape superclass:

class Circle(val radius: Double) : Shape() {
    override fun area(): Double {
        return Math.PI * radius * radius
    }
}

In this example, Circle is a subclass of Shape, and it overrides the area() method to provide its own implementation for calculating the area of a circle.

The super Keyword

In Kotlin, you can use the super keyword to access members (properties or methods) of the superclass within a subclass. This is useful when you want to invoke the superclass’s implementation of a method that you’ve overridden. Here’s an example:

open class Animal(val name: String) {
    open fun makeSound() {
        println("$name makes a sound")
    }
}

class Dog(name: String) : Animal(name) {
    override fun makeSound() {
        super.makeSound() // Calls the makeSound() method of the superclass
        println("$name barks")
    }
}

In this example, the Dog class inherits from the Animal superclass and overrides the makeSound() method. Inside the overridden method, super.makeSound() is used to call the makeSound() method of the Animal superclass before adding its own behavior.

Overriding Rules

In Kotlin, when you override a member (method or property) of a superclass in a subclass, you must follow some rules:

  1. The overridden member in the subclass must have the override modifier.
  2. The signature of the overridden member in the subclass must match the signature of the superclass member, including the name, return type, and parameter types.
  3. If the superclass member is marked as open, abstract, or interface, it can be overridden.
  4. You can use the super keyword to call the overridden member of the superclass within the subclass.
Polymorphism

Polymorphism is a key concept in inheritance, allowing you to work with objects of different classes in a unified way. In Kotlin, you can take advantage of polymorphism when dealing with class hierarchies. Here’s an example:

fun printArea(shape: Shape) {
    println("Area: ${shape.area()}")
}

fun main() {
    val circle = Circle(5.0)
    val square = Square(4.0)

    printArea(circle)
    printArea(square)
}

In this code, the printArea function takes a Shape object as an argument and prints its area. It can work with both Circle and Square objects because they inherit from Shape. This is an example of polymorphism, where objects of different subclasses can be treated as objects of the superclass.

Abstract Classes

In addition to regular classes, Kotlin allows you to define abstract classes. An abstract class is a class that cannot be instantiated on its own but can be used as a blueprint for other classes. Abstract classes may contain abstract methods, which are declared without implementation and must be overridden by subclasses. Here’s an example:

abstract class Shape {
    abstract fun area(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area(): Double {
        return Math.PI * radius * radius
    }
}

class Square(val sideLength: Double) : Shape() {
    override fun area(): Double {
        return sideLength * sideLength
    }
}

In this example, the Shape class is abstract, and it defines an abstract method area(). Subclasses Circle and Square provide their own implementations of the area() method. Abstract classes are useful for creating class hierarchies where certain methods need to be implemented by all subclasses.

Command and Example

Here’s a complete example that demonstrates inheritance and polymorphism in Kotlin:

open class Shape {
    open fun area(): Double {
        return 0.0
    }
}

class Circle(val radius: Double) : Shape() {
    override fun area(): Double {
        return Math.PI * radius * radius
    }
}

class Square(val sideLength: Double) : Shape() {
    override fun area(): Double {
        return sideLength * sideLength
    }
}

fun main() {
    val circle = Circle(5.0)
    val square = Square(4.0)

    println("Circle Area: ${circle.area()}")
    println("Square Area: ${square.area()}")
}

In this example, we have a superclass Shape with an area() method and two subclasses, Circle and Square, that override the area() method to provide their own implementations. The main() function demonstrates creating objects of both subclasses and invoking the area() method on them, showcasing how polymorphism allows for a unified approach to working with different shapes.