public interface OnClickListener {
void onClick(View v);
}
复制代码
public interface OnClickListener { view -> ... }
复制代码
这种方式能够工做的缘由是OnClickListener接口只有一个抽象方法。这种接口被称为函数式接口,或者***SAM
接口***,SAM
表明单抽象方法,Java API 中随处可见像Runnable
和Callable
这样的函数式接口,以及支持它们的方法。markdown
能够把lambda传给任何指望函数式接口的方法。ide
void postponeComputation(int delay, Runnable computation) 复制代码
在Kotlin中,能够调动用它并把一个lambda做为实参传给它。编译器会自动把它转换成一个Runnable的实例:函数
postponeComputation(1000) { println(42) }
复制代码
注意,“一个
Runnable
的实例”,指的是“一个实现了Runnable
接口的匿名类的实例”。编译器会帮助你建立它,并使用lambda
做为单抽象方法——这个例子中是run
方法——的方法体。post
经过显式地建立一个实现了Runnable的匿名对象也能达到一样的效果:测试
// 把对象表达式做为函数式接口的实现传递
postpostComputation(1000, object: Runnable {
override fun run() {
println(42)
}
})
复制代码
注意:this
- 在显式地声明对象时,每次调用都会建立一个新的实例。
- 使用lambda的状况不一样:若是lambda没有访问任何来自自定义它的函数的变量,相应的匿名类实例能够在屡次调用之间重用:
// 整个程序只会建立一个Runnable的实例 postponeComputation(1000) { println(42) } 复制代码
所以,彻底等价的实现应该是下面这段代码中的显式object声明,它把Runnable实例存储在一个变量中,而且每次调用的时候都使用这个变量:spa
// 编译成全局变量;程序中仅此一个实例
val runnable = Runnable { println(42) }
fun handleComputation() {
// 每次postponeComputation调用时用的是一个对象
postponeComputation(1000, runnable)
}
复制代码
若是lambda从包围它的做用域中捕获了变量,每次调用就再也不可能重用同一个实例了。这种状况下,每次调用时编译器都会建立一个新对象,其中存储着呗捕获的变量的值。code
// lambda会捕获“id”这个变量
fun handleComputation(id: String) {
// 每次handleComputation调用时都建立一个Runnable的新实例
postponeComputation(1000) { println(id) }
}
复制代码
注意:这里讨论的为lambda建立一个匿名类,以及该类的实例的方法只对指望函数式接口的Java方法有效,可是对集合使用Kotlin扩展方法的方式并不适用。若是把lambda传给了标记成***
inline
的Kotlin函数,是不会建立任何匿名类的。而大多数的库函数都标成了inline
***。orm
SAM构造方法是编译器生成的函数,让你执行从lambda到函数式接口实例的显式转换。能够在编译器不会自动应用转换的上下文中使用它。对象
例如,若是有一个方法返回的是一个函数式接口的实例,不能直接返回一个lambda,要用SAM构造方法把它保证起来。
// 定义
fun createAllDoneRunnable(): Runnable {
return Runnable { println("All done!") }
}
// 测试
>>> createAllDoneRunnable().run()
All done!
复制代码
SAM构造方法的名称和底层函数式接口的名称同样。SAM构造方法只接受一个餐宿——一个被用做函数式接口单抽象方法体的lambda——并返回实现了这个接口的类的一个实例。
Lambda和添加 / 移除监听器
注意lambda内部没有匿名类对象那样的this:没有办法引用在lambda转换成的匿名类的实例。从编译器的角度来看,lambda是一个代码块,不是一个对象,并且也不能把它当成对象引用。Lambda中的this引用指向的是包围它的类。