Interfaces in Kotlin

While working with Java, we had interfaces that would only have static variables or methods without the body. In Kotlin, however, now we can have interfaces with functions that contain a body.

Now, that being said, In this blog, we are going to learn how to define interfaces, use them, and what are the amazing things we can do with interfaces in Kotlin.

But first, let's see what are interfaces (in Java)?

An interface is like a abstract class that is used to group related methods with empty bodies. They only have methods inside it which is the implemented by a class to inherit all its methods.
An interface in java can have n-numbers of methods which are abstract and a class can implement any number of interfaces.

In this blog, we will learn,

  • How to declare and use interface in Kotlin?
  • Interfaces with properties and default implementation.
  • Properties in interface
  • Inheritance in interface
  • Implementing multiple interfaces.

How to declare and use interface in Kotlin?

In Kotlin we declare interfaces like,

interface ResponseListener {
    fun onSuccess(response: Response) 

    fun onError(error: Error) 
}

Here, we have an interface that has two abstract functions where Response and Error are data classes like,

data class Response(val data: String)
data class Error(val error: String)

Now, to use this interface, we need to implement it in class like,

class MyView() : ResponseListener {
    override fun onSuccess(response: Response) {

    }

    override fun onError(error: Error) {
        
    }

}

Here, we have created a class MyView that implements our interface and its functions are overridden in the class.

Interfaces with properties and default implementation

We can have a certain implementation to the functions in am interface itself and if we don't override them, the implementation works as default value.

Let's say we edit out interface like,

interface ResponseListener {
    fun onSuccess(response: Response) {
        Log.d("ResponseListener", "onSuccess")
    }

    fun onError(error: Error) {
        Log.d("ResponseListener", "onError")
    }
}

And if we implement it now to a class,

class MyView() : ResponseListener {
    override fun onSuccess(response: Response) {
        super.onSuccess(response)
    }

    override fun onError(error: Error) {
        super.onError(error)
    }
}

and initialize the class,

MyView().onError(Error(""))

It will print the following from onError() method with its default implementation,

ResponseListener: onError

Properties in interfaces

Similarly, how we have functions in interfaces we can also have different properties in it as well.

Properties can't have their own value as an interface can't have a state.

To set a value to a property we need to first implement it and then override value to it. Let us see this by example.

Consider an interface,

interface SetupAddition {
    val numOne: Int
    val numTwo: Int
    fun add() = numOne + numTwo
}

Here, we have two property numOne and numTwo. It also has a function add() which will return the sum of both numbers. Now, to use this we will implement it in a class like,

class Addition : SetupAddition {
    override val numOne: Int
        get() = 5
    override val numTwo: Int
        get() = 10

    override fun add(): Int {
        return super.add()
    }
}

and to get the addition, we create the object of the class like,

Addition().add()

This will print the sum 15.

Here, we first provided two values to the integers and then we called the add() function to return the addition.

Inheritance in Interface

We can inherit the property and function from one interface to another.

To understand it better, let's consider we have an interface,

interface Numbers {
    var numTwo: Int
    fun numOne(): Int // This is a function now
}

Here, we have one property and other is a function, and this Numbers interface is implemented by another interface like,

interface SetupAddition : Numbers {
    fun add() = numOne() + numTwo
}

Here, you can see in SetupAddition we can access both the function and property of Numbers interface and can perform the addition it the function of SetupAddition interface.

Now, we can use the above interface by,

class Addition() : SetupAddition {
    
    override fun numOne(): Int = 5
    override var numTwo: Int = 10
    
    override fun add(): Int {
        return super.add()
    }
}

Here, you can see that we have implemented the SetupAddition and it overrides the property of Numbers interface as well.

Implementing multiple interfaces

In Kotlin, we can implement multiple interfaces in a class and it will override functions from all interfaces implemented. For example,

Consider we have three interfaces like,

interface Numbers {
    var numTwo: Int
    fun numOne(): Int //This is a function
}

interface SetupAddition : Numbers {
    fun add() = numOne() + numTwo
}
interface ResponseListener {
    fun onSuccess(response: Response) {
        Log.d("ResponseListener", "onSuccess")
    }

    fun onError(error: Error) {
        Log.d("ResponseListener", "onError")
    }
}

Here, SetupAddition is inheriting Numbers and we have another interface called ResponseListener. Now, we implement it like,

class MyClass() : SetupAddition,ResponseListener {

    override fun numOne(): Int = 5
    override var numTwo: Int = 10

    override fun add(): Int {
        return super.add()
    }

    override fun onError(error: Error) {
        super.onError(error)
    }

    override fun onSuccess(response: Response) {
        super.onSuccess(response)
    }
}

In the above class, you can see that we have implemented SetupAddition and ResponseListener both interfaces to it.

Now, if we call the error method like,

MyClass().onError(Error(""))

Here it will print the output,

ResponseListener: onError

This is how we can implement and work with interfaces in Kotlin.