Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How kotlin makes setOnClickListener accept functions as parameter

Tags:

android

kotlin

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.

like image 890
err0r Avatar asked Dec 16 '16 08:12

err0r


People also ask

Can you pass functions as parameters in Kotlin?

In Kotlin, you can provide default values to parameters in function definition. If the function is called with arguments passed, those arguments are used as parameters.

How are parameters passed in Kotlin?

Only one parameter can be marked as vararg . If a vararg parameter is not the last one in the list, values for the subsequent parameters can be passed using named argument syntax, or, if the parameter has a function type, by passing a lambda outside the parentheses.

Can functions in Kotlin be assigned to variables?

Kotlin functions are first-class, which means they can be stored in variables and data structures, and can be passed as arguments to and returned from other higher-order functions.


1 Answers

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) { ... }
    
like image 58
Yoav Sternberg Avatar answered Nov 01 '22 06:11

Yoav Sternberg