一、作为函数的代码块
java的接口
public interface OnClickListener { void onClick(String v); }
1、使用java的方式进行创建匿名内部类的方式进行继承
OnClickListener listener = new OnClickListener() { @Override public void onClick(String v) { // 这里实现OnClickListener的函数 System.out.println("new OnClickListener():" + v); } }; listener.onClick("kk"); //输出:new OnClickListener():kk
2、使用java的方式进行创建lambda
OnClickListener listenerLambda = v -> { System.out.println("lambda:" + v); }; listenerLambda.onClick("qqq"); // 输出:lambda:qqq
java在使用lambda在继承方面看似也不错,很简洁
kotlin的接口
interface OnClickListener1 { fun onClick(v: String) } fun testClick2(clickListener1: OnClickListener1) { clickListener1.onClick("kkqqq") }
3、使用kotlin匿名内部类的方式
testClick2(object: OnClickListener1 { override fun onClick(v: String) { println("object: OnClickListener1:${v}") } }) // 输出:object: OnClickListener1:kkqqq
看着跟java的匿名内部类差不多
4、使用kotlin的方式创建lambda
这边在使用kotlin的lambda调用上述的testClick2,按照正常的想法应该是能够正确推理出:OnClickListener1 接口
testClick2 { println("lambda interface:$it")} // 这样子调用老是提示:Type mismatch.
这是为啥呢?
这里先改变一下testClick2的参数为上述java的接口
fun testClick1(clickListener: OnClickListener) { // OnClickListener是上述的java的接口 clickListener.onClick("kkqqq") } testClick1 { println("lambda interface:$it")} // 能正确输出:lambda interface:kkqqq
为啥使用java的接口可以但是kotlin的接口就不行呢?
官方的解释是 Kotlin 本身已经有了函数类型和高阶函数等支持,所以不需要再去转换。如果你想使用类似的需要用 lambda 做参数的操作,应该自己去定义需要指定函数类型的高阶函数。
二、SAM转换的概念
SAM转换的英文全拼是:Single Abstract Method Conversions。就是对于只有单个非默认抽象方法接口的转换 ,对于符合这个条件的接口在 Kotlin 中可以直接用 Lambda 来表示 (前提是 Lambda 的所表示函数类型能够跟接口中的方法相匹配)
例子:
上述的testClick1对应的接口是OnClickListener,对testClick1的传入参数是lambda表达式,该lambda表达式的类型是:(String) -> Unit , 正好跟OnClickListener接口的单一方法接口类型一样:void onClick(String v) ;所以testClick1的调用能够通过 :{ println("lambda interface:$it")} 替换OnClickListener作为testClick1的参数
如果SAM转换存在歧义该如何消除
1、这里有两个java接口
public interface OnClickListener { void onClick(String v); } public interface OnClickListener2 { void onClick(String v); }
还有两个对应的kotlin的函数,参数分别是上述的OnClickListener和OnClickListener2
fun testClick1(clickListener: OnClickListener) { clickListener.onClick("kkqqq") } // 这里使用重载testClick1函数 fun testClick1(clickListener: OnClickListener2) { clickListener.onClick("kkqqq") }
如果直接调用:testClick1 {println("lambda interface:$it")} 则会提示:Overload resolution ambiguity.
这里的正确调用:
方法一:使用lambda的方式:testClick1 (OnClickListener{println("lambda interface:$it")}) 该方法首先创建了一个`OnClickListener`的匿名实例,然后对其唯一的方法进行了实现。
方法二:使用匿名的内部类方式
testClick1(object: OnClickListener { override fun onClick(v: String?) { println("lambda interface:$v") } })
方法三、testClick ({it: String -> println("lambda interface:$it")} as OnClickListener) 但是这个方法在运行的时候提示:cannot be cast to class OnClickListener。原因是因为:在Kotlin中,不能直接将Lambda表达式强制类型转换为Java SAM接口,即使该接口是一个函数接口