Using lambdas does not compile when trying to pass in a method expecting a SAM interface

前端 未结 2 1424
旧时难觅i
旧时难觅i 2021-01-26 04:03

I am trying to understand lambdas and Kotlin. I created this trivial example

interface OnClickListener {
    fun onClick(s: String)
}

class Button {
    var cli         


        
相关标签:
2条回答
  • 2021-01-26 04:56

    To be able to use a lambda, you need to use a Java interface.

    First, create a Java file and create an interface:

    public interface OnClickListener {
       void onClick(String s);
    }
    

    Then in your main:

       b.setOnClickListener(OnClickListener { s ->
            println(s)
       })
    

    As for your Button class:

    class Button {
     var clickListener: OnClickListener? = null //You can use this too but there's another way as well.
    
     //lateinit var clickListener: OnClickListener //Telling the compiler that you will initialize it later on.
    
     fun setOnClickListener(listener: OnClickListener) { //removed redundant ? from the function signature.
         clickListener = listener
     }
      fun click() {
         clickListener?.onClick("hello")  //Incase of lateinit, you don't need a '?' anymore
      }
    }
    

    SAM conversion only works between a Java code and a Kotlin code.

    EDIT: Since in Kotlin, you can store a function in a variable as well, here is my another two cents on how you can do it in a different way:

    class Button {
       lateinit var myFunction: (String) -> Unit
    
       fun setOnClickListener(block : (String) -> Unit) {
          myFunction = block //storing state of your 'listener'
       }
    
       fun onClick() = myFunction.invoke("Invoked from onClick function")
    }
    

    Then in your main:

    fun main() {
       val button = Button()
       button.setOnClickListener { s ->
           println(s)
       }
    
       button.onClick()
    }
    
    0 讨论(0)
  • 2021-01-26 04:56

    As Taseer Ahmad points out, SAM conversion only works for Java interfaces since Kotlin already has proper function types. Of course, an easy way around this is to simply define a second setOnClickListener method that takes a function type

    class Button {
        var clickListener: OnClickListener? = null
    
        fun setOnClickListener(listener: OnClickListener?) {
            clickListener = listener
        }
    
        inline fun setOnClickListener(crossinline listener: (String) -> Unit) {
            setOnClickListener(object : OnClickListener { 
                override fun onClick(s: String) = listener(s)
            })
        }
    
        fun click() {
            clickListener?.onClick("hello")
        }
    }
    

    This then allows you to write b.setOnClickListener { println(it) }. I always inline methods like this as a habit, but it's not really required, so you can remove the inline and crossinline if you want.

    0 讨论(0)
提交回复
热议问题