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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Interfaces and Abstract Classes in Kotlin: Getting Started
25 mins
- Getting Started
- Inheritance
- Abstract Classes
- Characteristics of Abstract Classes
- Implementing the Animal Abstract Class
- Interfaces
- Characteristics of Interfaces
- Example of Interfaces Application
- Default Methods of an Interface
- Implementing Interfaces in the Project
- The Practical Side of Interfaces
- Holding State Inside an Interface
- Abstract Classes vs Interfaces
- Where to Go From Here?
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.
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:
Select Lion and you will see:
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:
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
.
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?
-
Animal, an open class, defines the animal’s
name
,image
, preferredfood
andhabitat
.getHungerAmount()
defines how hungry the animal is. -
Mammal, another open class, extends from Animal and defines one more property,
furColor
, while implementing all the previous properties fromAnimal
. -
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.
- 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. - 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.
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. - 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.
Now that you’ve seen how abstract classes work, it’s time to update the code above.