Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a listener object as a function parameter in kotlin

I'm trying to pass a listener from an action to a class (an adapter).

In java (code from the Action):

  private void setListeners() {
    adapterRecyclerView.setListener(
            new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    SomeCodehere....
                }
            });
}

(code from the adapter)

public void setListener(View.OnClickListener listener) {
    this.listener = listener;
}

It works.

Now I'm trying to traslate to kotlin. I translate first the action (translation the action to kotlin):

    private fun setListeners() {
    // !! is not fine i know
    adapterRecyclerView!!.setListener  { v  ->
                          SomeCodehere....
    }
}

At this point still works. With the code of the adapter still in java and code of the class in kotlin. Now I translate the adapter to kotlin:

fun setListener(listener: View.OnClickListener) {
    this.listener = listener 
}

Now it doesn't work. The Action does not compile.

Error: cannot infer a type for this parameter "v". required View.OnClickListener. found (???) Unit.

How I must do the cast here? Why passing the parameter from kotlin to java works and from kotlin to kotlin it does not?

like image 536
Kunta Kinte Avatar asked May 25 '17 21:05

Kunta Kinte


2 Answers

Update: see Kotlin SAM support: https://kotlinlang.org/docs/java-interop.html#sam-conversions for most updated information.

In the case of calling Java code you are benefitting from SAM conversion for single method interfaces written in Java. Then when you port the interface to Kotlin it does not allow this yet (Kotlin currently assumes you would use function references and lambdas instead of a single method interface).

The problem is the same as from this other similar question: Android - Kotlin - object must be declared abstract or implement abstract member

Since this is a Kotin interface, you cannot use the SAM conversion to a Lambda so that is why the other answer previously provided does not work. If this was a Java interface, you could do that. You can track SAM conversions for Kotlin interfaces in KT-7770.

If you wanted this code to be more idiomatic Kotlin you would want function references or lambdas instead of interfaces, and you should just do that instead of relying on SAM conversion. You can read more about that in Higher-Order Functions and Lambdas. This is outside the scope of your question to go into more detail.

Therefore as mentioned in another answer by @joakim, you must pass in a instance of a class that implements this interface. This is called an Object Expression and looks like:

object : View.OnClickListener {
    override fun onClick(v: View) {...}
})

Or realistically you should change your Kotlin port of the code to accept a reference to a function so that a lambda can be passed in directly. That would be more idiomatic and you would be able to call it as you were originally attempting.

like image 62
5 revs Avatar answered Oct 18 '22 14:10

5 revs


Change

adapterRecyclerView!!.setListener  { v  ->
                      SomeCodehere....
}

to

adapterRecyclerView!!.setListener(object : View.OnClickListener {

})

and implement the methods of View.OnClickListener

like image 37
Joakim Avatar answered Oct 18 '22 14:10

Joakim