Lambda表达式是java8以后更新的,用于简化实现函数式接口的匿名内部类。所谓函数式接口指接口只有一个抽象方法(接口也可以有默认方法、私有方法、类方法,但抽象方法只能有一个)。
当需要把行为作为参数传递给方法时,可以把方法参数写成函数式的接口类型,然后使用Lambda表达式创建实现类对象作为实参传入该方法。
Lambda表达式的核心用途就是简化代码,简化带来的副作用是不够直观,但如果掌握了省略的方式,代码看起来会很优雅。
Lambda表达式语法
通用语法格式为:
(参数列表) -> {方法体内语句}
因为Lambda表达式用于创建函数式接口的实现类对象,接口的类型可以看被调用方法的形参确定,所以可以直接省略。
而函数式接口只有一个抽象方法,所以只需要重写该抽象方法即可。对于被重写的抽象方法而言,方法名是固定的,在接口的定义处可以确认,直接省略。所以只需要写出方法的参数列表,再配上实现类重写的方法体部分,就可以完成函数式接口的实现类定义。
所以可以看到,通用语法左边是参数列表部分,对应抽象方法方法头的有效信息,右边是方法体的执行语句,对应抽象方法方法体的核心信息。所以上面的语法格式看似怪异,其实是足够简化的结果。
用匿名内部类和对应的Lambda表达式举例:
@FunctionalInterface
interface InterA {
int add(int a, int b);
}
public class LambdaDemo1 {
public static void main(String[] args) {
// 这里传入匿名内部类创建的对象
userInterA(new InterA() {
@Override
public int add(int a, int b) {
return a + b;
}
});
// 省略后的对应Lambda表达式,左边是抽象方法的形参列表,右边是重写的方法体
// 参照上面,可以完全找到对应关系,但下面的代码明显更简洁
userInterA((int a, int b) -> {
return a + b;});
}
public static void userInterA(InterA a) {
int num = a.add(6, 5);
System.out.println(num);
}
}
Lambda表达式的省略
为了更加的简化,Lambda表达式还可以进一步简化,以下是简化的规则:
- 可以省略形参列表的形参类型。
其实很好理解,因为形参类型在接口定义部分已经固定,没必要重写。 - 如果形参只有一个参数,可以省略小括号。
- 如果方法体只有一行语句,可以省略花括号,注意同时也要去掉语句结尾的分号 。如果只有的这行语句是返回语句,必须省略return关键字,系统会检测到该方法需要返回值,会直接把表达式的结果作为返回值返回。
方法引用与构造器引用
Lambda表达式如果要重写抽象方法的方法体只有一行,而且满足下面的条件,就可以进一步简写,分为以下情况:
1. 引用类方法
如果Lambda表达式的格式为:(a,b,…) -> 类名.类方法(a,b,…)
注意,形参列表的所有参数会全部作为引用类方法的对应参数,这时就可以简写为:类名::类方法
-
引用特定对象的实例方法
如果Lambda表达式的格式为:(a,b,…) -> 特定对象名.实例方法(a,b,…)
注意,形参列表的所有参数会全部作为引用特定对象的实例方法的对应参数,这时就可以简写为: 特定对象名::实例方法 -
引用某个对象的实例方法
如果Lambda表达式的格式为:(a,b,c,…) -> a.实例方法(b,c,…)
注意,参数列表中第一个参数作为调用者,其余参数全部作为右边的对应参数,这时就可以简写为: 类名::实例方法, 这里的类名是第一个参数的类型。 -
引用构造器
如果Lambda表达式的格式为:(a,b,…) -> new 类名(a,b,…)
注意,参数列表的所有参数作为调用的构造器的对应参数,这时就可以简写为:类名::new
Lambda表达式的常见使用场景
分为三种:
- 赋给函数式接口类型的引用变量
- 作为函数式接口类型的参数传给某个方法
- 使用函数式接口对Lambda表达式进行强制类型转换
Lambda表达式的类型是不固定的,完全取决于是实现哪个函数式接口,所以Lambda表达式创建的实现类对象根据使用场景的位置会发生变化。
Lambda表达式与匿名内部类
Lambda表达式本质就是简写函数式接口对应的匿名内部类,如果匿名内部类是为了实现函数式接口,就可以用Lambda表达式代替。
只是Lambda表达式只能重写抽象方法,不能增加新的方法。
而且Lambda表达式创建的对象虽然可以继承接口的默认方法,但是Lambda表达式里不能调用该接口中的默认方法。
但很多匿名内部类可以做的事情,Lambda表达式也可以完成:
- 都可以直接访问"effectively final"的局部变量,以及外部类的成员变量(包括实例变量和类变量,在类方法里的当然只能访问类变量)。
- 创建的对象都可以直接调用从接口继承到的默认方法。
Lambda表达式与注解
因为Lambda表达式只能作为函数式接口的实现类对象,所以可以在函数式接口定义前加上注解,如果接口不是函数式接口,就会报错。
注解为:@FunctionalInterface