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?
Implementing Interfaces in the Project
Animals have different abilities. For example, kangaroos can jump very high, cheetahs can run extremely fast and some fish can survive in the ocean’s greatest depths. However, animals often have similar abilities.
In this project, you’ll implement three main abilities animals share: Walking, flying and swimming. These abilities are represented by the following interfaces: Walkable, Flyable and Swimmable.
While it may seem straightforward, animal categories are complicated. For example, not every mammal can walk and not every bird can fly. So Mammal can’t define member runningSpeed() if not all mammals can walk or run. You must implement these specific behaviors separately.
At the root of the project, create a package and name it interfaces. In this package, create the following interfaces in their respective files:
Walkable.kt
interface Walkable {
val runningSpeed: Int
}
Walkable.kt
interface Flyable {
val flyingSpeed: Int
}
Walkable.kt
interface Swimmable {
val swimmingSpeed: Int
}
Every interface implements a speed member representing how fast the animal can go. In this example, the code shows a single property for every interface, but feel free to play around with the code and add some abstract methods and methods with bodies.
Finally, every animal has to implement the right interface. Let Bat and Parrot implement Flyable. Then set their flyingSpeeds to 95 and 30 respectively. For instance, update Bat with:
class Bat : Mammal(), Flyable
and …
override val flyingSpeed: Int
get() = 95
Let Elephant, Giraffe and Lion implement Walkable. Then set their runningSpeeds to 40, 60 and 80 respectively. For instance, update Elephant with:
class Elephant : Mammal(), Walkable
override val runningSpeed: Int
get() = 40
Lastly, let Penguin implement both Walkable and Swimmable. Then set the runningSpeed and swimmingSpeed to 7 and 40 respectively. Here are the required changes:
class Penguin : Bird(), Walkable, Swimmable
and …
override val runningSpeed: Int
get() = 7
override val swimmingSpeed: Int
get() = 40
Here’s a code breakdown:
-
Bat,Parrot,Elephant,GiraffeandLionimplement a single interface and redefine a single method. -
Penguinhas two skills. It can swim and run, even though he’s pretty slow. That’s why it’s crucial to have the ability to implement multiple interfaces.
The Practical Side of Interfaces
While you now understand why your future projects should implement interfaces, it’s often hard to grasp this argument in a practical way. In this section, you’ll explore the practical side of interfaces.
As you may have noticed, Zoo Guide is still missing an important feature, the filter.

The Spinner lets you choose between the three different behaviors defined above, Walkable, Flyable and Swimmable, and an All option that includes all the animals in the list. When you select one of these options, the list updates.
Navigate to ui/fragments package. Then open ListFragment.kt. Inside onItemSelected(), replace the TODO with:
val updatedList: List<Animal> = when(position) {
0 -> AnimalStore.getAnimalList()
1 -> AnimalStore.getAnimalList().filter { it is Flyable }
2 -> AnimalStore.getAnimalList().filter { it is Walkable }
3 -> AnimalStore.getAnimalList().filter { it is Swimmable }
else -> throw Exception("Unknown animal")
}
viewModel.updateItem(updatedList)
Here’s a code breakdown:
- Anytime a user clicks an item inside the Spinner,
onItemSelected()is called. - The
whenstatement checks the position and filters the list based on the item the user selects. -
ListViewModeldefinesupdateItem(). You need to call it every time you update the list to notify the adapter that something has changed.
filter will check every Animal object in the list and choose only the one that respects the behavior the user selected.
Now you can see how an interface isn’t only a contract to implement methods and properties but also creates a sub-category of objects. You can instantiate a group of objects, such as arrays or lists, that share the same behavior. They may be completely different objects, but they share the same behavior.
Holding State Inside an Interface
If you’ve searched for interfaces on the internet, you’ve likely encountered some blogs that say interfaces can’t hold states. That was true until Java 8 introduced methods with a body inside interfaces. But, what’s the state?
The state is what an object has. For example, the name property inside Bird defines a state of a Bird object. If an object doesn’t have any instance properties or has some properties with unknown values that don’t change, it’s stateless.
Where can you store the state? You store it in a companion object property.
Consider the code below:
// 1
interface Animal {
var name: String
get() = names[this]?.toUpperCase() ?: "Not in the list"
set(value) { names[this] = value }
var speed: Int
get() = speeds[this] ?: -1
set(value) { speeds[this] = value }
val details: String
get() = "The $name runs at $speed km/h"
companion object {
private val names = mutableMapOf<Animal, String>()
private val speeds = mutableMapOf<Animal, Int>()
}
}
// 2
class Lion: Animal
// 3
class Penguin: Animal
// 4
fun main() {
val lion = Lion()
lion.name = "Lion"
lion.speed = 80
val penguin = Penguin()
penguin.name = "Penguin"
penguin.speed = 7
println(lion.details) // The LION runs at 80 km/h
println(penguin.details) // The PENGUIN runs at 7 km/h
}
Similar to Zoo Guide, this code will save a list of animals.
Here’s what the code does:
- Every animal has three properties: a
name,speedanddetails. The first two are mutable while the last one is immutable. - Both
nameandspeedcan get and store a value using aMutableMap. Note that thekeyof theMapis a reference to the Animal interface itself that changes every time a class implements it. - The Animal interface has two private static mutableMaps that hold the state.
- Both
LionandPenguinimplementAnimal. They don’t have to define any members ofAnimalsince all of them have been initialized.
Change the names‘s visibility from private to public and iterate the list:
// 1
interface Animal {
companion object {
val names = mutableMapOf<Animal, String>()
}
}
// 2
Animal.names.forEach { name ->
println(name.value)
}
//Output:
//Lion
//Penguin
As you can see, Animal.names holds all the values.
The scope of this example was only to show you that it’s possible to store the state inside an interface. However, you shouldn’t do that in real-world projects because it’s poorly managed and not cleaned properly by the garbage collector.