尝试传递需要SAM接口的方法时,使用lambdas无法编译

时间:2019-09-13 09:26:00

标签: kotlin lambda functional-programming

我想了解lambda和Kotlin。我创建了这个简单的例子

interface OnClickListener {
    fun onClick(s: String)
}

class Button {
    var clickListener: OnClickListener? = null

    fun setOnClickListener(listener: OnClickListener?) {
        clickListener = listener
    }
    fun click() {
        clickListener?.onClick("hello")
    }
}

fun main(args: Array<String>) {
    val b = Button()
    b.setOnClickListener(
        object : OnClickListener {
            override fun onClick(s: String) {
                println(s)
            }

        }
    )

   /* 
   Variation 1
   val l = {
        s -> println(s)
    }
    b.clickListener = l*/

    /*
    Variation 2

    b.setOnClickListener{
        s -> println(s)
    }
    */

    /*
    Variation 3

    b.clickListener = {
            s -> println(s)
    }
    */

    b.click()
}

因此,仅当我传递匿名对象时,以上代码才会编译。但是我想弄清楚如何使用lambda。 使用lambda编译的3个变体都没有。
我以为OnClickListener是SAM,所以我应该很容易就能通过lambda 我在这里做什么错了?

2 个答案:

答案 0 :(得分:3)

要使用lambda,您需要使用Java接口。

首先,创建一个Java文件并创建一个接口:

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

然后在您的main中:

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

关于您的Button班:

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转换仅在Java代码和Kotlin代码之间起作用。

编辑:由于在Kotlin中,您也可以将函数存储在变量中,这是我的另外两分钱,以另一种方式来实现它:

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")
}

然后在您的main中:

fun main() {
   val button = Button()
   button.setOnClickListener { s ->
       println(s)
   }

   button.onClick()
}

答案 1 :(得分:1)

正如Taseer Ahmad指出的那样,由于Kotlin已经具有适当的函数类型,所以SAM转换仅适用于Java接口。当然,解决此问题的一种简单方法是简单地定义第二个使用函数类型的setOnClickListener方法

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")
    }
}

然后,您可以编写b.setOnClickListener { println(it) }。我总是习惯使用内联这样的方法,但这并不是必需的,因此您可以根据需要删除inlinecrossinline