Interfaces and Abstract Classes in Kotlin: Getting Started

Learn how to best use interfaces and abstract classes to create class hierarchies in your Kotlin Android apps. By Mattia Ferigutti.

4.5 (4) · 1 Review

Download materials
Save for later
Share

Before Object-oriented programming (OOP), coding was more complex because languages like C don’t let you abstract concepts at a high level. Abstraction helps you structure a project by grouping common characteristics together to create an object. For example, fur color, tail length and age are characteristics that make a cat.

At its core, abstraction makes coding simpler by providing tools to define real-world objects. It also has other benefits like making code easier to read, understand and maintain. However, it can also complicate your life.

In this tutorial, you’ll explore the differences between Abstract Classes and Interfaces and learn when to use them. You’ll create Zoo Guide, an app which shows information about different animals. Along the way, you’ll learn:

  • About Inheritance.
  • How to implement abstract classes and interfaces.
  • The differences between abstract classes and interfaces.
  • Create an application which makes use of abstract classes, interfaces and inheritance.
Note: This article assumes you’re familiar with the basics of Kotlin. If you’re new to Kotlin, look at Programming in Kotlin before you start.

Getting Started

Download the materials by clicking Download Materials at the top or bottom of this tutorial. Open Android Studio and select Open. Then, select the starter project.

Now, run the project and check its behavior:

Animal list screen

Select Lion and you will see:

Animal details screen

The app has two screens:

  • The main screen shows a list of animals you can click.
  • A second screen shows some details about the animal you’ve selected.

This is how the project is structured:

Structure of the project

As you can see, the project includes six animals: Bat, elephant, giraffe, lion, parrot and penguin. There are also two main categories: Bird and Mammal. Finally, there’s the Animal class.

Before you start coding, take a moment to learn about inheritance.

Inheritance

Inheritance defines an is-a relationship between a parent class and its subclasses. Every non-private member of a parent class is visible inside the subclasses if preceded by the keyword open.

Note: In Kotlin, a class and its members are final by default. So, if you want to extend them you have to add the keyword open.

Have a look at the Animal, Mammal and Lion classes to see a demonstration of how inheritance works:

// 1
open class Animal(
    open val name: String,
    @DrawableRes open val image: Int,
    open val food: Food,
    open val habitat: Habitat
) {
  
  open fun getHungerAmount() : Int {
    return 0
  }
  
  //...
}

// 2
open class Mammal(
    override val name: String,
    @DrawableRes override val image: Int,
    override val food: Food,
    override val habitat: Habitat,
    val furColor: List<Int>
) : Animal(name, image, food, habitat)

// 3
class Lion : Mammal(
    "Lion",
    R.drawable.animal_elephant_sample,
    Food.ZEBRAS,
    Habitat.SAVANNA,
    listOf(Color.parseColor("#CB9C70"))
) {
  
  override fun getHungerAmount() : Int {
    return (0..100).random()
  }
}

So, what’s going on here?

  1. Animal, an open class, defines the animal’s name, image, preferred food and habitat. getHungerAmount() defines how hungry the animal is.
  2. Mammal, another open class, extends from Animal and defines one more property, furColor, while implementing all the previous properties from Animal.
  3. Lion extends Mammal and again, inherits all of Mammal‘s properties and implements getHungerAmount().

This is the principle of Inheritance. It is an OOP concept, and a basic way of establishing relationships between classes. There are three main issues with this approach:

Overriding getHungerAmount() in Lion was optional; you didn’t have to implement it. The compiler wouldn’t give you any errors if you didn’t.

  1. Since Animal and Mammal are open, you can create instances of them, which doesn’t make any sense since they’re only abstract concepts and not concrete, like Lion. This irregularity is extremely dangerous.
  2. You need the subclasses to implement the methods and properties from the parent class. In this case, you can use the constructor to force the subclasses to implement a property from the parent class, but you can’t do the same thing with methods.
  3. There are certain characteristics that some mammals have but others don’t. Classes can’t support this type of behavior.

What a mess. But what if abstract classes and interfaces could help you solve all these issues?

Abstract Classes

You can see abstract classes as a mixture of interfaces and regular classes. Abstract classes can have everything that interfaces have as well as properties and constructors. Therefore, you can properly hold state in abstract classes, but you can’t instantiate an abstract class.

You can think of an abstract class as a template. Think about the WordPress templates catalog, where you can choose a specific template with all the functions you need to build a website.

Maybe you want an e-commerce website with different built-in characteristics like a reviews section, cart and list of products. You choose the template that suits you and edit a few things to make it exactly as you want. Done!

Well, that was much simpler than creating the website from scratch.

Abstract classes do the same thing. They provide a common behavior for all subclasses. Use an abstract class to create a group of characteristics that many classes have in common.

Characteristics of Abstract Classes

Abstract classes can’t be final because the main reason they exist is for other classes to extend them. However, their methods and properties can be final. In fact, they’re final by default.

Abstract classes can have both regular and abstract methods or properties. Regular methods and properties can have a body and a value, correspondingly.

In contrast, something defined as abstract doesn’t have a defined value yet. In the case of methods, that means it doesn’t have a body.

Whenever a class extends an abstract class with abstract members, the class must implement all the abstract members. This is essential when a subclass has to implement a method to create a specific behavior.

Take a look at this example:

// 1
abstract class Wrestler {
    abstract fun themeMusic(): String
}

// 2
class JohnCena : Wrestler() {
    override fun themeMusic() = "johncena_theme.mp3"
}

In this example, every wrestler has their own themeMusic that you have to define every time you create a new wrestler. When extending Wrestler, the compiler will force the JohnCena class to implement themeMusic() so you don’t forget to.

Unlike interfaces, abstract classes can have a constructor and thus an init block. Either of these are essential to initialize something when an object is created.

Note: Keep in mind, if a parent class hasn’t initialized a member, you’ll have to initialize it in the subclass.

Now that you’ve seen how abstract classes work, it’s time to update the code above.