2018-05-11Lambda表达式
Lambda表达式入门
Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例,这种语法避免了匿名内部类的烦琐。
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
Lambda表达式就相当于一个匿名方法,它由3部分组成。
- 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
- 箭头(->)。必须通过英文中画线和大于符号组成。
- 代码块。如果代码块中只包含一条语句,Lambda表达式允许省略代码块的花括号。Lambda代码块只有一条return语句,甚至可以省略return关键字。若Lambda表达式需要返回值(取决于目标类型是否需要返回值),而他的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。
Lambda表达式与函数式接口
Lambda表达式的类型,也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
如果采用匿名内部类语法来创建函数式接口的实例,则只需要实现一个抽象方法,在这种情况下即可采用Lambda表达式来创建对象,该表达式创建出来的对象的目标类型就是这个函数式接口。查询java8的API文档,可以发现大量的函数式接口,例如:Runnable、ActionListener等接口都是函数式接口。
由于Lambda表达式的结果就是被当成对象,因此程序中完全可以使用Lambda表达式进行赋值,例如如下代码。
//Runnable接口中只包含一个无参数的方法
//Lambda表达式代表的匿名方法实现了Runnable接口中唯一的、无参数的方法
//因此下面的Lambda表达式创建了一个Runnable对象
Runnable r=()->{
for(int i=0;i<100;i++)
{
System.out.println();
}
};
由于Lambda表达式实现的是匿名方法——因此它只能实现特定函数式接口中的唯一方法,这意味着Lambda表达式有如下两个限制:
- Lambda表达式的目标类型必须是明确的函数式接口。
- Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口(函数式接口)创建对象。
关于上面第一点限制,看下面代码是否正确:
Object obj=()->{
for(int i=0;i<100;i++)
{
System.out.println();
}
};
上面代码与前一段代码几乎完全相同,只是此时程序将Lambda表达式不再赋值给Runnable变量,而是直接赋值给Object变量。编译上面代码,会报如下错误:
不兼容的类型:Object不是函数接口
从该错误信息可以看出,Lambda表达式的目标类型必须是明确的函数式接口。为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下三种常见方式。
- 将Lambda表达式赋值给函数接口类型的变量。
- 将Lambda表达式作为函数式接口类型的参数传递给某个方法。
- 使用函数式接口对Lambda表达式进行强制类型转换。
注意
同样的Lambda表达式的目标类型完全可能是变化的,唯一的要求是,Lambda表达式实现的匿名方法与目标类型(函数式接口)中唯一的抽象方法有相同的形参列表。
方法引用与构造器引用
如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。方法引用和构造器引用都需要使用两个英文冒号。Lambda表达式支持如下表所示的几种引用方式