Kotlin Polymorphism and Inheritance: Essential Concepts for Android Developers

Kotlin Polymorphism and Inheritance: Essential Concepts for Android Developers

Understanding and Utilizing Polymorphism and Inheritance in Kotlin for Effective Development

Welcome back to the third article in this series of Android Development with Kotlin and Jetpack Compose. In the previous article, we covered the fundamentals of object-oriented programming in Kotlin - including classes, properties, methods and objects

In this article, we will learn about inheritance and polymorphism, and how are these concepts useful in Android development. By the means of this article, we will also discuss interfaces and abstract classes. Let's get started!

Introduction to Inheritance and polymorphism

Inheritance and Polymorphism are important concepts in the object-oriented programming paradigm. Let's look at how each one is important.

Inheritance

Inheritance is a way to create new classes that are derived from existing classes. This is particularly useful because these derived classes have access to the properties and methods of the base class.

For instance, imagine that you are building an app for a school. You might have a class called Person that has information about a person, like their name and age.

Then, you can create new classes for specific types of people, like students and teachers. These new classes will inherit all the information from the Person class and you can add more specific information to them as well.

For example, the Student class might have information about what grade the student is in, and the Teacher class might have information about what subject the teacher teaches.

Inheritance is a useful tool in programming because it allows us to reuse code and create more specialized classes without having to write all the code from scratch. This can save us a lot of time and make our app more efficient.

Polymorphism

Polymorphism is a way of creating objects that can take on different forms. It gives us the ability to make sure that the classes of same category conform to a similar structure.

Imagine that you are building an app that has different types of animals, like dogs, cats, and birds. Each type of animal has its own unique characteristics, like the way they move, the sounds they make, and the things they like to do.

But they also have some things in common, like the fact that they are all animals and they can make sounds - although these sounds will depend on the type of animal all animals can make sounds.

Polymorphism allows us to create a template or plan for an animal that includes all the things that all animals can do. Then, we can create specific implementations for each type of animal, and give them the unique characteristics that are specific to that type of animal.

This is useful in programming because it allows us to create code that can be used in different situations and can adapt to different types of objects. It helps us to write more efficient and flexible code.

So with this understanding of inheritance and polymorphism, Let's look at how we can implement them in Kotlin.

Inheritance in Kotlin

As we saw earlier, inheritance is the practice of creating new classes by deriving them from existing classes. In Kotlin, we need to tell the compiler that we will be using a certain class to create a new class derived from it; to do this, we use the open keyword.

Imagine we are developing an application for a library. We would need a Book class that with properties such as title, author, and isbn. And we would also need a Movie class with properties such as title, director, and releaseYear.

Instead of having two separate classes with similar properties, we can create a parent Media class with the shared properties and have the Book and Movie classes inherit from it. This would allow you to reuse the shared code and keep your code organized.

open class Media {
    var title: String = ""
    var releaseYear: Int = 0

    fun getDescription(): String {
        return "$title ($releaseYear)"
    }
}

Now, to create a class that inherits from Media class, place the name superclass (Media in this case) after : in the declaration of the derived class

class Book: Media() {
    var author: String = ""
    var isbn: String = ""
}

Similarly, here's how you can create the Movie class we discussed above

class Movie: Media() {
    var director: String = ""
    var runtime: Int = 0
}

Overriding methods

Now that we have specialized classes for Books and Movies, we will need to make their descriptions a little more specific. For that, we will override the getDescription method.

To make a method overridable, we need to mark it with open keyword as we did with classes

open class Media {
    var title: String = ""
    var releaseYear: Int = 0

    open fun getDescription(): String {
        return "$title ($releaseYear)"
    }
}

Now, we will add the specialized method in the child classes.

class Book: Media() {
    var author: String = ""
    var isbn: String = ""

    override fun getDescription(): String {
        return "$title by $author ($releaseYear) [ISBN: $isbn]"
    }
}

class Movie: Media() {
    var director: String = ""
    var runtime: Int = 0

    override fun getDescription(): String {
        return "$title directed by $director ($releaseYear, $runtime minutes)"
    }
}

Polymorphism in Kotlin

As we saw earlier, polymorphism is the practice of creating objects that can take on different forms. To do this, we can use a normal class, an abstract class, or an interface as the base and build specific implementations by deriving from them.

Let's go with the example of different animals from above. Imagine that you are building an app that has different types of animals, like dogs, cats, and birds. Each type of animal has its unique characteristics, like the way they move, the sounds they make, and the things they like to do.

In this situation, we could use polymorphism to create a base Animal class with shared properties and behaviors, and then create subclasses for each specific type of animal. This would allow us to define the unique characteristics of each animal in the subclass, while still being able to treat them all as Animal objects.

Let's see how we can implement this code by first using abstract classes and then by using interfaces.

Using Abstract Classes for Polymorphisms

As per Kotlin docs,

A class may be declared abstract, along with some or all of its members. An abstract member does not have an implementation in its class. You don't need to annotate abstract classes or functions with open.

Here's how we would implement the base Animal class as an abstract class

abstract class Animal {
    var name: String = ""

    abstract fun move(): String
    abstract fun speak(): String
}

Since the class is abstract, we don't need to implement the methods move and speak. Now, let's create our specialized animal classes from this.

class Dog: Animal() {
    override fun move(): String {
        return "$name the dog is running and playing."
    }

    override fun speak(): String {
        return "$name the dog is barking."
    }
}

class Cat: Animal() {
    override fun move(): String {
        return "$name the cat is stalking its prey."
    }

    override fun speak(): String {
        return "$name the cat is meowing."
    }
}

class Bird: Animal() {
    override fun move(): String {
        return "$name the bird is flying."
    }

    override fun speak(): String {
        return "$name the bird is chirping."
    }
}

Using Interfaces for Polymorphism

Interfaces in Kotlin are used to define a set of abstract methods that must be implemented by a class that wants to implement the interface. Interfaces are similar to abstract classes, but they can only contain abstract methods and cannot contain any concrete methods with an implementation. We can also have properties in interfaces but they will only have the getter when accessed as an interface

Here is how we can define an Animal as an interface instead of an abstract class.

interface IAnimal {
    val name: String

    fun move(): String
    fun speak(): String
}

Now to create a concrete implementation of the above interface we will need to override move, speak and name. Here's how we can implement the above animals using interfaces:


class Dog (
    override val name: String
): IAnimal {
    override fun move(): String {
        return "$name the dog is running and playing."
    }

    override fun speak(): String {
        return "$name the dog is barking."
    }
}

class Cat(
    override val name: String
): IAnimal {
    override fun move(): String {
        return "$name the cat is stalking its prey."
    }

    override fun speak(): String {
        return "$name the cat is meowing."
    }
}

class Bird(
    override val name: String
): IAnimal {
    override fun move(): String {
        return "$name the bird is flying."
    }

    override fun speak(): String {
        return "$name the bird is chirping."
    }
}

Benefit of polymorphism

Now, the main benefit of polymorphism is ability to change form in runtime. Here's a sample of polymorphism in action

fun main() {    
    val animals: List<IAnimal> = listOf(
        Dog("Henry"),
        Cat("Tom"),
        Bird("Bubbles")
    )

    for (animal in animals) {
        println(animal.speak())
    }
}

The above code outputs:

As you can see we declared all the creatures with the type IAnimal but during runtime, they conform to their specific types i.e Dog, Cat and Bird

Conclusion

In conclusion, polymorphism and inheritance are important concepts in object-oriented programming languages like Kotlin. Polymorphism allows for flexibility and code reuse, as a single class can take on multiple forms and behave differently in different situations. Inheritance allows for the creation of new classes that are derived from existing ones, allowing for code reuse and organization.

Together, polymorphism and inheritance can be used to create complex and flexible object-oriented systems, making it easier to manage and maintain code. Developers need to understand these concepts and know how to use them effectively when designing and building applications.

Keep Coding!