1 简介
Lambda表达式允许把函数作为一个方法的参数,Lambda表达式本质是一个函数的语法糖。Java作为“一切都对象”的忠实拥趸,在引入lambda语法时,自然不能与其他语言“同流合污”把lambda当成函数。
所以Java中Lambda表达式与函数式接口进行绑定,lambda表达式本质上就是函数式接口的实例。(还是对象,维护了自己的傲娇)
2 函数式接口
- 只包含一个抽象方法的接口,可以用
@FunctionalInterface
进行注解。 - 函数式接口既可以通过实例执行重写的方法,也可以作为其他函数的形参,在其他函数中调用重写的方法。
- 在
java.util.function
包下定义了很多函数式接口,最核心的4个函数式接口是:
// 消费型接口 Consumer<T> void accept()
// 供给型接口 Supplier<T> T get()
// 函数型接口 Function<T, R> R apply(T t)
// 断定型接口 Predicate<T> boolean test(T t)
此外还有些函数式接口不是定义在java.util.function
包中,如:
// 可执行接口 Runnable void run()
// 比较器接口 Comparator<T> int compare(T o1, T o2)
3 Lambda表达式
基本语法:
(parameters) -> { statements; }
- 箭头左边是
形参列表
,参数类型可以省略(参数推断),如果只有一个参数,括号也可以省略。 - 箭头右边是
lambda体
,如果只有一条语句则可以省略花括号{}
,如果lambda体只有一条return语句,return
也可以省略。
lambda表达式作为函数式接口实例的语法糖,实现其抽象方法。
如:
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world");
}
};
Runnable runnable2 = () -> System.out.println("Hello world");
runnable1.run(); // 打印输出 Hello world
runnable2.run(); // 打印输出 Hello world
注意: lambda体内访问的局部变量必须是final
修饰或相关的。否则会报异常:
Local variable xxx defined in an enclosing scope must be final or effectively final.
可以使用数组避免该异常,如:
// double[] --> Map<Integer, Double>
double[] arr = {1.1, 3.2, 5.5, 6.4, 9.9};
Integer[] index = {1};
Map<Integer, Integer> map = Arrays.stream(arr).boxed()
.collect(Collectors.toMap(item -> index[0]++, Double::new));
4 方法引用
- 方法引用(Method References)本质上还是lambda表达式,也是函数式接口实例。
- 主要用于
lambda体
只调用了一个其他类的方法,该方法的形参和返回类型与函数式接口的抽象方法的形参和返回类型相同。 - 格式:使用
::
操作符引用方法:类名::方法名
、对象::实例方法名
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
Comparator<String> com2 = String::compareTo;
System.out.println(com1.compare("abc", "def")); // -3
System.out.println(com2.compare("abc", "def")); // -3
方法引用也适用于构造方法
和数组[]
:类::new
、类型[]::new
Function<Integer, Person> fun = age -> new Person(age);
Function<Integer, Person> fun2 = Person::new;
System.out.println(fun.apply(100));
System.out.println(fun2.apply(101));