前言
lambda表达式采用的是一种简洁的语法,来传递代码块。使得java能够支持函数式编程。
问题引入
我们先来看一个简单的java API:
Arrays.sort(T[] a, Comparator<? super T> c)
其中,a是待排序数组,c是一个实现了Comparator接口的比较器对象。
在没有lambda表达式之前,我们需要自己创建一个类并实现Comparator接口的compare方法,并在调用Arrays.sort时传入我们所创建的这个比较器的对象。
如:
class LengthComparator implements Comparator<String>
{
public int compare(String first, String second)
{
return first.length() - second.length();
}
}
... ...
String[] strings = new String[]{"aaa", "bbb", "ccc"};
Arrays.sort(strings, new LengthComparator());
这样做的目的仅仅是为了让sort方法能够在排序时调用我们自定义的compare方法,显然上述方法是很麻烦且不够优雅的。
于是,lambda表达式这个新特性应运而生,它的作用,便是直接传递一个代码块
lambda表达式的语法
参数表 -> {代码体}
例如:(String s1, String s2)-> {return s1.length() - s2.length()}
- 当lambda表达式不需要传参时,你仍需要提供一个空括号,例如:
() -> {'hello lambda'}
- 如果能够从上下文推导出参数类型,可省去参数类型,例如:
(s1, s2) -> {s1.length() - s2.length()}
- 如果参数表仅有一个参数,且这个参数的类型也可以推导出来,那么可以直接省去括号和类型
s -> {s.length()}
- 有时甚至可以省略掉return语句,lambda表达式的返回值可以从上下文中推导得出
e -> {e == null}
lambda表达式的存储
对于任何类型,我们都可以声明一个变量来存储它的值,那么我们该用什么类型的变量来存储lambda表达式这样一个代码块呢。
java的设计者并没有选择像其他类型的编程语言一样,增加函数类型,而是仍然采用接口类型,来存储lambda表达式。这个特殊的接口叫做函数式接口
**
这个函数式接口必须有且仅有**一个抽象方法(可有其他静态方法,默认方法),该方法的返回值即为lambda表达式的返回值,该方法的参数表即为lambda表达式的参数表,
换句话说,lambda表达式便可以看作这个抽象方法的实现。
注意,lambda表达式只能赋给函数式接口,甚至不能将它赋给Object类
于是,当我们想给某个方法传入一个代码块时,只需要保证该方法有着与你想要传入的lambda表达式相匹配的函数式接口类型的参数即可。
让我们再回到开头的那个例子:
Arrays.sort(T[] a, Comparator<? super T> c)
现在我们调用这个sort方法,不必再像之前那样繁琐,直接可以这样:
String[] strings = new String[]{"aaa", "bbb", "ccc"};
Arrays.sort(strings, (first, second) ->
{first.length() - second.length()});
方法引用 (Method Reference)
如果你的lambda表达式仅仅是调用另外一个另外一个方法,那么你可以传入一个方法引用而不是lambda表达式
示例:
//创建一个定时器,周期性打印事件对象
var time = new Timer(1000, System.out::println)
需要注意的是,传入的方法引用的参数表要与该函数式接口的抽象方法的参数表一致
运算符 :: 分隔对象名(类名)和方法名。有以下三种情况:
- 对象 :: 实例方法
- 类 :: 实例方法
- 类 :: 静态方法
特别指出对于第二种情况,该函数式接口的抽象方法中,第一个参数的类型必须是方法引用中的那个类
闭包
先看这样一个例子:
public static void repeatMessage(String text, int delay)
{
ActionListener listener = event -> {
System.out.print(text);
}
}
其中,lambda表达式的代码块内部使用了代码块外部的自由变量,
这里我们先介绍以下lambda表达式的组成:
- 一个代码块({}中的内容)
- 参数表
- 自由变量的值 (即不是代码块中声明的变量,也不是参数表中的变量,而是外部的变量)
当外部变量,也就是自由变量,被lambda表达式所使用后,它的值便不允许改变,也就是说这个变量从变量变成常量了
关于代码块以及自由变量有一个术语叫做闭包,不仅仅是python有闭包,java也有闭包