javalambdakotlinhigher-order-functionskotlin-interop

Using functional interfaces with function types in Kotlin


When calling Java code from Kotlin, there is SAM conversion so that Java code like this:

adapter.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view, int position) {
        // Do stuff here
    }
});

Can look like this:

adapter.setOnClickListener { view, position ->
    // Do stuff
}

Now, I'm working on a Kotlin project and I want to define a functional interface as an event listener:

interface OnSomeActionListener {

    fun onSomeAction(parameter1: Int, parameter2: String)

}

In SomeClass I have a function to set the listener:

    ...

    private var onSomeActionListener: OnSomeActionListener? = null

    fun setOnSomeActionListener(listener: OnSomeActionListener) {
        onSomeActionListener = listener
    }

    ...

And when I create an instance of this class and try to invoke the setter function, I do it like so:

val thing = SomeClass()

thing.setOnSomeActionListener(object : OnSomeActionListener {
    override fun onSomeAction(parameter1: Int, parameter2: String) {
        // Do stuff here
    }
})

I'm aware that Kotlin has function types therefore doesn't support SAM conversion from various sites such as this one.

I've read a little about function types but I have not used them before.

How would I rewrite my code so that I can invoke the setter function like this?

val thing = SomeClass()

thing.setOnSomeActionListener { parameter1, parameter2 ->
    // Do stuff here
}

.


Solution

  • A function type looks like this:

    (Parameters) -> ReturnType
    

    In your case, instead of using the interface type, you could use (View, Int) -> Unit. It would look something like this:

    private var onSomeActionListener: ((View, Int) -> Unit)? = null
    
    fun setOnSomeActionListener(listener: (View, Int) -> Unit) {
        onSomeActionListener = listener
    }
    
    private fun callSomeActionListener(view: View, position: Int) {
        onSomeActionListener?.invoke(view, position)
    }
    

    Add names

    In functional types you can also specify names for the parameters. This doesn't change much under the hood but they can add some clarity here and in the calling code, which is nice.

    (view: View, position: Int) -> Unit
    

    Using a type alias

    To avoid having to type (View, Int) -> Unit every time, you can define a typealias:

    typealias OnSomeActionListener = (view: View, position: Int) -> Unit
    

    So that your code now looks like this again:

    private var onSomeActionListener: OnSomeActionListener? = null
    
    fun setOnSomeActionListener(listener: OnSomeActionListener?) {
        onSomeActionListener = listener
    }   
    

    And to call it:

    val thing = SomeClass()
    
    thing.setOnSomeActionListener { view, position ->
        // Do stuff here
    }