- 函数式编程
- 采用简介的语法定义代码块
lambda 表达式是一个可传递的代码块,可以在以后执行一次或多次。
lambda 表达式的语法
--
函数式接口
有且只有一个抽象方法的接口被称为函数式接口(function interface),通常使用@FunctionalInterface
注解标记。
对于函数式接口,需要这种接口的对象时,就可以提供一个 lambda 表达式。
最好把 lambda 表达式看作一个函数,而不是一个对象。lambda 表达式可以传递到函数式接口,在调用这个接口的抽象方法的时候会执行 lambda 表达式的体。
如果设计自己的接口,其中只有一个抽象方法,可以用@FunctionalInterface
注解来标记这个接口。这样做有两个优点。如果无意中增加了另一个非抽象方法,编译器会产生一个错误消息。另外 javadoc 页里会指出该接口是一个函数式接口。
@FunctionalInterface
并不是函数式接口必须使用的注解。依据定义,任何有且只有一个抽象方法的接口都是函数式接口。不过使用@FunctionalInterface
注解是一个很好的做法。
方法引用
如果已经有现成的方法可以完成想要传递到其他代码的某个动作,那么可以使用方法引用。
方法引用就是使用::
操作符分割方法名与对象或类名,主要有三种情况:
object::instanceMethod
Class::staticMethod
Class::instanceMethod
在前两种情况中,方法引用等价于提供方法参数的 lambda 表达式。例如,System.out::println
等价于x -> System.out.println(x)
。对于第三种情况,第一个参数会成为方法的目标(实例对象)。例如,String::compareToIgnoreCase
等同于(x, y) -> x.compareToIgnoreCase(y)
。
类似于 lambda 表达式,方法引用也不能独立存在,总是会转换为函数式接口的实例。
构造器引用
构造器引用与方法引用很类似,只不过方法名为new
。具体使用哪一个构造器取决于上下文。
变量作用域
lambda 表达式有 3 个部分:
- 代码块
- 参数
- 自由变量的值,这是指非参数而且不在代码块种定义的变量
为了能够使用在 lambda 表达式外定义的变量(自由变量),lambda 表达式的数据结构就必须存储自由变量的值,可以说这个变量被 lambda 表达式捕获(captured)。例如,可以把一个 lambda 表达式转化为包含一个方法的对象,这样自由变量的值就会复制到这个对象的实例变量中。
在 Java 中,lambda 表达式可以捕获外围作用域的值,但要确保所捕获的值是明确定义的。在 lambda 表达式中,只能引用值不会改变的变量,无论这个变量是在 lambda 表达式中改变还是在外部改变,都是不合法的。
lambda 表达式中捕获的变量必须实际上是最终变量。实际上的最终变量是指,这个变量初始化之后就不会再为它赋新值。
在一个 lambda 表达式中使用this
关键字时,是指创建这个 lambda 表达式的方法的this
参数。
public void Application {
public void init() {
ActionListener listener = event -> {
System.out.println(this.toString());
...
}
}
}
表达式this.toString()
会调用Application
对象的toString
方法,而不是ActionListener
实例的方法。
处理 lambda 表达式
使用 lambda 表达式的重点时延迟执行。毕竟,如果想要立即执行代码,完全可以直接执行,而无需把它包装在一个 lambda 表达式中。之所以希望延迟执行代码,有很多原因,如:
- 在一个独立的线程中运行代码;
- 多次运行代码;
- 在算法的适当位置运行代码(例如,排序中的比较操作);
- 发生某种情况时执行代码(例如,点击按钮,数据到达,等等);
- 只在必要时才运行代码。
-- 常用函数式接口
-- 基本类型的函数式接口
网友评论