GHH
GHH

Reputation: 2019

How can I convert to lambda with my Custom Class?

I use Kotlin and practice lambda expression.

In normal, View.setOnClickListener can convert to lambda

Normal

textView.setOnClickListener(object :View.OnClickListener{
    override fun onClick(p0: View?) {

    }
})

Lambda

textView.setOnClickListener { }

then I copy the source code and just rename the function

class CustomView{

    fun setCustomOnClickListener(l: CustomOnClickListener) {
        throw RuntimeException("Stub!")
    }

}

interface CustomOnClickListener {
    fun customOnClick(var1: View?)
}

and I create my customView but it can't be convert to lambda

      val myCustomView = CustomView()

      myCustomView.setCustomOnClickListener(object :CustomOnClickListener{

          override fun customOnClick(var1: View?) {

          }
      })

  //  can't convert to 
  //  myCustomView.setCustomOnClickListener{
  //
  //  } 

Can anyone explains why and how to convert to lambda expression?

Thanks!!

Upvotes: 1

Views: 2135

Answers (4)

HiddenDroid
HiddenDroid

Reputation: 1450

I think as of the date this question was posted, it wasn't possible to do so in Kotlin. But now it's available in Kotlin starting from version 1.4 with functional interfaces.

Here is an example on how to do it:

// Define your interface here (note the 'fun' modifier prior to 'interface')
fun interface CustomOnClickListener {
    fun customOnClick(view: View)
}

// Call your custom interface in the form of a Lambda
myCustomView.setCustomOnClickListener { view ->
    // Implement your logic here
}

For more information about functional interfaces, you can visit the following link https://kotlinlang.org/docs/fun-interfaces.html#sam-conversions

Upvotes: 0

Nero
Nero

Reputation: 121

I have converted spinner's OnItemSelectedListener using lambda function as parameter check it out where I only care about onItemSelected method.

import android.view.View
import android.widget.AdapterView

class SelectionListener(val lis:(parent: AdapterView<*>?, view: View?, position: Int, id: Long) -> Unit) : AdapterView.OnItemSelectedListener {


  override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {

    lis(parent,view, position, id)

  }

  override fun onNothingSelected(parent: AdapterView<*>?) { }

}

Now attach it to your spinner in activity / fragment

my_spinner.onItemSelectedListener = SelectionListener { parent, view, position, id ->
//do your work after selection
}

Upvotes: 0

user1713450
user1713450

Reputation: 1429

What you're asking about is called a SAM conversion (converting an actual interface implementation into a lambda). SAM stands for "single abstract method."

You can only do SAM conversions for Java interfaces (which View.OnClickListener is). But your CustomOnClickListener is a Kotlin interface. Therefore, you cannot do a SAM conversion. You must implement it with object : CustomOnClickListener { override . . . }. You cannot use a lambda.

Straight from the Kotlin docs:

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

What you could do if you wanted to keep your code concise is, instead of creating interface CustomOnClickListener you could do

typealias CustomOnClickListener = (View?)->Unit

then your setter function would be the same. Your invoking function would then be this.myListener(myView)

or you could even use the experimental inline class instead:

inline class CustomOnClickListener(val customOnClick: (View?)->Unit)

then your setter would be

fun setListener(listener: CustomOnClickListener) { this.listener = listener }

and your invoking code would be

listener.customOnClick(someView)

Edit Some more fleshed-out code:

class CustomView{
    var listener: CustomOnClickListener? = null

    fun setCustomOnClickListener(l: CustomOnClickListener) {
        listener = l
    }

}

And then you can do this:

inline class CustomOnClickListener(val customOnClick: (View?)->Unit)

and then your custom view:

val myCustomView = CustomView()

myCustomView.setCustomOnClickListener(CustomOnClickListener({ it: View? ->
    // whatever your listener is supposed to do with the view, it goes here
}))

Or, instead of inline class (which is still considered an experimental feature in Kotlin), you can do this:

typealias CustomOnClickListener = (View?)->Unit

and then your custom view:

val myCustomView = CustomView()

myCustomView.setCustomOnClickListener { it: View? ->
    // whatever your listener is supposed to do with the view, it goes here
}

Here is an example.

Upvotes: 5

Mayokun
Mayokun

Reputation: 1184

Just like @user1713450 said, this concept is called SAM Conversion and this works for only Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.

To explain your code above:

The object keyword can be used to create objects of an anonymous class known as anonymous objects. They are used if you need to create an object of a slight modification of some class or interface without declaring a subclass for it.

The correct syntax should be:

interface CustomOnClickListener {

fun customOnClick(var1: View?)

}

//Use the interface in your class
class CustomView{

fun setCustomOnClickListener(l: CustomOnClickListener) {
    throw RuntimeException("Stub!")

  }

}

To use the CustomView class:

val customView = CustomView()

  customView.setCustomOnClickListener(object: CustomOnClickListener{

      override fun customOnClick(var1: View?) {

      }
  })

Upvotes: 0

Related Questions