Implement Kotlin Interfaces with SAM conversions

Different ways of implementing Kotlin interfaces - using subclass conventional method, object keyword and functional (SAM) interfaces

ยท

2 min read

Implement Kotlin Interfaces with SAM conversions

While experimenting LiveData observer, I encountered the following code and I had no idea how it works.

val observer = Observer<T> {
    state.value = it
}

So I looked at the Observer source code in Java. It looks like this.

public interface Observer<T> {
    void onChanged(T t);
}

Observer is an interface, and observer is an object that implements the Observer interface. Wait, where is override fun onChanged(t: T)? How is that possible?

It turns out this is called SAM conversions. SAM stands for Single Abstract Interface.

Before we look into SAM conversions, let's first look at different ways of implementing the interfaces.

Subclass Conventional Method

This is the interface,

interface Observer<T> {
    fun onChanged(t: T)
}

and we subclass it with ObserverImpl

class ObserverImpl<T> : Observer<T> {
    override fun onChanged(t: T) {
        println("$t")
    }
}

To instantiate the ObserverImpl, call the onChanged() function:

val observer = ObserverImpl<String>()
observer.onChanged("test")

Object Keyword

Other than creating singleton class, object keyword can also be used to implement an interface.

Instead of subclassing, you can implement the Observer interface directly

val observer = object: Observer<String> {
    override fun onChanged(t: String) {
        println("$t")
    }
}
observer.onChanged("test")

SAM Conversions

Before we can use SAM conversions, you need to add fun keyword in front of the interface.

fun interface Observer<T> {
    fun onChanged(t: T)
}

This is called Functional Interface or Single Abstract Method (SAM) Interface. In other word, the interface must have only ONE function/method.

To implement the Observer interface, you use the lambda expression.

val observer = Observer<String> {
    println("$it")
}
observer.onChanged("test")

This lambda expression is basically the override onChanged() function that you want to implement. it is the implement argument.

If onChanged() has 2 arguments,

fun interface Observer<T> {
    fun onChanged(arg1: T, arg2: T)
}

it is going to be like this

val observer = Observer<String> { arg1, arg2 ->
    println("$arg1,$arg2")
}
observer.onChanged("test1", "test2")

Conclusion

Well, SAM conversions are new to me. After getting used to it, I just need to imagine the lambda expression as the override function of the interface I want to implement.

It uses less code than object keyword because you no longer need to explicitly declare the override function. The limitation is the interface must contain only one interface.

Did you find this article valuable?

Support Vincent Tsen by becoming a sponsor. Any amount is appreciated!

ย