简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。
1.Java 8 的 Lambda
在 Java 8 之前我们使用 Thread 可能是这样的:
new Thread(new Runnable() {
public void run() {
System.out.println("test");
}
}).start();
在 Java 8 之后开始支持 Lambda 表达式,于是我们可以这样写:
new Thread(()->System.out.println("test")).start();
从 Runnable 的源码可以发现,它使用了 @FunctionalInterface,@FunctionalInterface 是 Java 8 为函数式接口引入的一个新的注解。表明该接口是函数式接口,只包含唯一一个抽象方法。任何可以接受一个函数式接口实例的地方,都可以用 Lambda 表达式。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Java 8 的 Lambda 表达式是一个 SAM(Single Abstract Method) 类型。因为 Java 没有像 Kotlin 那样拥有函数类型,所以需要借助 SAM 类型来实现 Lambda 表达式。
Java 8 的 Lambda 语法:
(参数列表) -> {函数体}
对于无参的 Lambda 表达式:
() -> {函数体}
对于单个参数的 Lambda 表达式:
(x) -> {函数体}
它可以简化为:
x -> {函数体}
对于多个参数的 Lambda 表达式:
(x,y,z) -> {函数体}
如果函数体只有一行代码,则可以省略函数体的大括号。另外对于参数列表,由于 Java 的编译器可以进行类型推断,所以不需要声明参数的类型。
在 Java 8 中,Lambda 的创建是通过字节码指令invokedynamic
来完成的,减少了类型和实例的创建消耗。在 Java 8 之前,我们使用匿名类,但是匿名类需要创建新的对象。
2.Kotlin 的 Lambda 语法
Kotlin 的 Lambda 表达式使用一对大括号括起来,后面依次跟着各个参数及其类型,紧接着是" -> ",函数体定义在" -> "之后。函数体的最后一句的表达式结果就是 Lambda 表达式的返回值。如果 Lambda 表达式没有参数的话,可以省略" -> "。
Kotlin 的 Lambda 语法:
{
参数列表 -> 函数体
}
使用 Kotlin 来实现上述代码:
Thread { println("test") }.start()
看上去,Kotlin 的 Lambda 语法相比 Java 的 Lambda 语法似乎更加简洁。
3.简化 Kotlin 的 Lambda 表达式
Kotlin 的 Lambda 表达式可以不断地被简化。以处理 Android View 的点击事件为例,使用 Java 的代码大致如下:
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
...
}
});
按照 Java 的风格来编写 Kotlin 的代码,使用 Kotlin 的对象表达式:
view.setOnClickListener(object :View.OnClickListener{
override fun onClick(v: View?) {
...
}
})
再对上述代码使用 Lambda 表达式:
view.setOnClickListener({ v ->
...
})
如果参数为函数类型并且是最后一个参数,那么可以将参数移到函数的括号外面:
view.setOnClickListener() { v ->
...
}
如果参数只有一个 Lambda 表达式,那么函数的小括号可以省略:
view.setOnClickListener { v ->
...
}
最后,在点击事件里如果会使用到 view,那么" v -> "可以省略,使用默认参数it
进行替代:
view.setOnClickListener {
it.visibility = View.GONE
...
}
4.总结
小结一下简化 Lambda 表达式的规则:
- 在函数中,最后一个参数是函数类型,那么可以将 Lambda 可以移到函数的括号外面。
- 如果函数的参数只有一个 Lambda ,那么函数的小括号可省略。
- 在 Lambda 表达式中只有一个参数,可以使用默认参数 it 进行替代。
- 对于有多个参数的 Lambda 表达式,如果某个参数未使用,可以用下划线("_")取代其名称
- 入参、返回值与形参一致的函数,可以用方法引用的方式作为实参传入。