In kotlin, we can use setOnClickListener()
like this:
view.setOnClickListener { println("Hello") }
But if I define my own interface, I can only pass anonymous object like this:
obj.setMyListener(object: MyListener() {
...
})
I just wondering how they make setOnClickListener()
accept a function rather than an anonymous object.
According to Kotlin documentations about Java interop, for a functional interface defined in Java, you can use a SAM conversion.
Just like Java 8, Kotlin supports SAM conversions. This means that
Kotlin function literals can be automatically converted into
implementations of Java interfaces with a single non-default method,
as long as the parameter types of the interface method match the
parameter types of the Kotlin function.
val runnable = Runnable { println("This runs in a runnable") }
val executor = ThreadPoolExecutor()
// Java signature: void execute(Runnable command)
executor.execute { println("This runs in a thread pool") }
However, Kotlin has functional types, therefore SAM conversion doesn't work for interfaces defined in Kotlin:
Also note that this feature works only for Java interop; since Kotlin
has proper function types, automatic conversion of functions into
implementations of Kotlin interfaces is unnecessary and therefore
unsupported.
Possible solutions:
- Define the interface in Java. Useful if it is a library/code that may be used from a Java code.
Make the method receive a function as argument:
// Example listener receives a bool and return unit.
fun setMyListener(listener: (isChecked: Bool) -> Unit) { ... }
// Usage:
obj.setMyListener { isChecked -> }
Use type alias (only supported in Kotlin 1.1+):
typealias MyListener = (Bool) -> Unit
fun setMyListener(listener: MyListener) { ... }