Lambda表达式简介
Java 8新引入Lambda表达式。
注意Lambda表达式只能用来替代函数式接口(函数式接口是只包含有一个抽象方法的接口,哪怕有很多个默认方法,只有只有一个抽象方法,就是函数式接口)!甚至不能把Lamda表达式赋值给Object!即仍然保持使用接口的习惯,只是将接口的实现类编写简化成Lambda表达式。具体来说,Lambda表达式其实是函数式接口中的函数的实现。
推荐给所有的函数式接口上加上@FunctionalInterface,表示这是一个函数式接口。
举例说明:
Comparator是一个接口,主要用于Arrays类的静态排序方法sort里指定排序的规则。
比如如果希望对字符串进行排序,排序规则为字符串的长度。如果不用Lambda表达式的话,我们一般会用一个匿名内部类来实现:
//创建Compartor接口的匿名实现
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(),o2.length());
}
};
//调用comparator对象进行排序
String[] words = new String[]{"l1111","l222"};
Arrays.sort(words,comp);
Lambda表达式的作用即在于替代上面的匿名实现部分,即:
//使用Lambda表达式写函数式接口实现
Comparator<String> comp = (first,second)->Integer.compare(first.length(),second.length());
固使用Lambda表达式有两个条件:
- 定义好的函数式接口
- 定义好的调用该函数式接口的方法
当然,由于Lambda表达式一般是“一次性用品”,所以一般我们会这样写:
Arrays.sort(words,(first,second)->Integer.compare(first.length(),second.length()));
由于隐去了接口对象的声明,同时隐去了变量类型的声明,所以有一些代码显得不明所以。
最后提示:
- 不需要声明参数的类型
- 不需要声明返回值的类型,在简单的情况下甚至不需要使用return。在比较复杂的情况下,可以像写方法一样写Lambda表达式,使用(x1,x2)->{ /*注意这里是花括号 */ };的形式写lambda表达式
- Lambda表达式可以没有限制地使用参数、实例变量和静态变量,但是如果想使用局部变量则必须显式地使用final修饰
Lambda表达式和闭包
闭包就是一个函数的实例,且它可以无限制地访问那个函数的非本地变量。例如,闭包可以作为参数传递给另一个函数。它也可以访问和修改其作用域之外的变量。
现在, Java 8的Lambda和匿名类可以做类似于闭包的事情:它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但有一个限制:它们不能修改定义Lambda的方法的局部变量的内容。这些变量必须是隐式最终的。可以认为Lambda是对值封闭,而不是对变量封闭。
这种限制存在的原因在于局部变量保存在栈上,并且隐式表示它们仅限于其所在线程。如果允许捕获可改变的局部变量,就会引发造成线程不安全的新的可能性,而这是我们不想看到的(实例变量可以,因为它们保存在堆中,而堆是在线程之间共享的) 。