Kotlin基础知识(九)——使用Java函数式接口

  • Java示例
public interface OnClickListener {
        void onClick(View v);
}
复制代码
  • Kotlin示例
public interface OnClickListener { view -> ... }
复制代码

这种方式能够工做的缘由是OnClickListener接口只有一个抽象方法。这种接口被称为函数式接口,或者***SAM接口***,SAM表明单抽象方法,Java API 中随处可见像RunnableCallable这样的函数式接口,以及支持它们的方法。markdown

1、把lambda看成参数传递给Java方法

能够把lambda传给任何指望函数式接口的方法。ide

  • 示例1、有一个Runnable类型的参数的方法:
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

2、SAM构造方法:显式地把lambda转换成函数式接口

SAM构造方法是编译器生成的函数,让你执行从lambda到函数式接口实例的显式转换。能够在编译器不会自动应用转换的上下文中使用它。对象

例如,若是有一个方法返回的是一个函数式接口的实例,不能直接返回一个lambda,要用SAM构造方法把它保证起来。

  • 使用SAM构造方法来返回值
// 定义
fun createAllDoneRunnable(): Runnable {
    return Runnable { println("All done!") }
}

// 测试
>>> createAllDoneRunnable().run()
All done!
复制代码

SAM构造方法的名称和底层函数式接口的名称同样。SAM构造方法只接受一个餐宿——一个被用做函数式接口单抽象方法体的lambda——并返回实现了这个接口的类的一个实例。

Lambda和添加 / 移除监听器

注意lambda内部没有匿名类对象那样的this:没有办法引用在lambda转换成的匿名类的实例。从编译器的角度来看,lambda是一个代码块,不是一个对象,并且也不能把它当成对象引用。Lambda中的this引用指向的是包围它的类。

相关文章
相关标签/搜索