How kotlin makes setOnClickListener accept functio

2019-02-16 15:23发布

问题:

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.

回答1:

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:

  1. Define the interface in Java. Useful if it is a library/code that may be used from a Java code.
  2. 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 -> }
    
  3. Use type alias (only supported in Kotlin 1.1+):

    typealias MyListener = (Bool) -> Unit
    fun setMyListener(listener: MyListener) { ... }