Implement Kotlin Interfaces with SAM conversions
Different ways of implementing Kotlin interfaces - using subclass conventional method, object keyword and functional (SAM) interfaces
Table of contents
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.