2014年3月18日发布了JavaSE 8
不追求技术的新,追求技术的稳定
本质:Lambda 表达式是一个匿名函数
作用:简化代码,增强代码的表达力
Lambda 语法格式
1 | // 格式1:无参无返回值 |
2 | () -> System.out.println("Hello World!"); |
3 | |
4 | // 格式2:有参无返回值 |
5 | (x) -> System.out.println(x); |
6 | |
7 | // 格式3:有参有返回值 |
8 | (x) -> x * x; |
9 | |
10 | // 格式4:多参有返回值 |
11 | (x, y) -> x + y; |
12 | |
13 | // 格式5:函数体包含多条语句 |
14 | (x, y) -> { |
15 | System.out.println("加法运算"); |
16 | return x + y; |
17 | } |
Lambda 表达式中的参数的数据类型可以省略,JVM 编译器能够根据上下文推算出,即“类型推断”
两个例子
1 | /** 1. Comparator **/ |
2 | TreeSet<Integer> ts1 = new TreeSet<>(new Comparator<Integer>(){ |
3 | |
4 | public int compare(Integer i1, Integer i2) { |
5 | return Integer.compare(i1, i2); |
6 | } |
7 | }); |
8 | // lambda 表达式 |
9 | TreeSet<Integer> ts2 = new TreeSet<>((i1, i2) -> { |
10 | return Integer.compare(i1, i2); |
11 | }); |
12 | // 等同于(使用方法引用还可以再次简化) |
13 | TreeSet<Integer> ts3 = new TreeSet<>((i1, i2) -> Integer.compare(i1, i2)); |
14 | |
15 | |
16 | /** 2. Runnable */ |
17 | Thread t1 = new Thread(new Runnable(){ |
18 | |
19 | public void run() { |
20 | System.out.println("当前线程:" + Thread.currentThread().getName()); |
21 | } |
22 | }); |
23 | // lambda 表达式 |
24 | Thread t2 = new Thread(() -> { |
25 | System.out.println("当前线程:" + Thread.currentThread().getName()); |
26 | }); |
函数式接口
!!Lambda 表达式需要函数式接口的支持
函数式接口:接口只有一个抽象方法
可以使用注解 @FunctionalInterface 修饰接口,检查是否是函数式接口
1 | // 定义函数式接口 |
2 | |
3 | public interface Calculator<T> { |
4 | public T calculate(T x, T y); |
5 | } |
6 | |
7 | // 使用函数式接口 |
8 | Calculator<Integer> calculatorAdd = (x, y) -> x + y; |
9 | Integer result = calculatorAdd.calculate(3, 5); |
Java 内置了四大核心函数式接口:
消费型接口 Consumer<T>:消费一个参数对象
1 | |
2 | public interface Consumer<T> { |
3 | void accept(T t); |
4 | ... |
5 | } |
供给型接口 Supplier<T>:返回一个对象
1 | |
2 | public interface Supplier<T> { |
3 | T get(); |
4 | } |
函数型接口 Function<T, R>:传递一个参数,返回一个值
1 | |
2 | public interface Function<T, R> { |
3 | R apply(T t); |
4 | ... |
5 | } |
断定型接口 Predicate<T>:判断参数是否满足约束
1 | |
2 | public interface Predicate<T> { |
3 | boolean test(T t); |
4 | ... |
5 | } |
对于Java内置函数式接口建议结合 stream 方法理解,在这里了解即可
除了这4大核心函数式接口外,还有由它们延伸出的一些变种,比如二元消费型接口 BiConsumer<T, U>
1 | public interface BiConsumer<T, U> { |
2 | void accept(T t, U u); |
3 | ... |
4 | } |
方法引用
将 lambda 体代码封装为方法,然后方法引用,再次简化代码。
方法引用格式:类名::方法名 或 对象::方法名
温馨提示:
实际上,在开发工具 IDEA 中,会自动提示使用方法引用简化代码,你只需按
ALT+Eenter快捷键,根据提示选择操作即可如果你想要深入了解方法引用的使用原则,可以继续往下看。(即使不看也没大问题,有开发工具帮你优化)
使用方法引用改写 Comparator 例子中的 lambda 表达式
1 | // |
2 | TreeSet<Integer> ts3 = new TreeSet<>((i1, i2) -> Integer.compare(i1, i2)); |
3 | // 使用方法引用 |
4 | TreeSet<Integer> ts4 = new TreeSet<>(Integer::compare); |
(第一种情况)实现函数式接口方法的参数列表,必须和方法引用方法的参数列表保持一致
即 Comparator.compare(o1, o2) 的 o1, o2 与 Integer.compare(i1, i2) 中的 i1, i2 对应,所以才能够使用方法应用。
当函数式接口方法只有一个参数时(小例子):
1 | |
2 | |
3 | public void test3() { |
4 | List<String> stringList = Arrays.asList("北京", "天津", "上海"); |
5 | // `Consumer.accept(t)` 的参数 t 与 `System.out.println(o)` 的 o 对应 |
6 | show(System.out::println, stringList); |
7 | } |
8 | // 自定义一个函数 |
9 | void show(Consumer<String> consumer, List<String> list) { |
10 | for (String s : list) { |
11 | consumer.accept(s); |
12 | } |
13 | } |
还有第二种情况
1 | TreeSet<Integer> ts3 = new TreeSet<>((i1, i2) -> i1.compareTo(i2)); |
2 | // 使用方法引用 |
3 | TreeSet<Integer> ts4 = new TreeSet<>(Integer::compareTo); |
Comparator.compare(o1, o2) 的 o1, o2 与 i1.compareTo(i2) 中 i1, i2 对应,这样也能使用方法引用。
(第二种情况)假设函数式接口方法参数有 (x1, x2),而方法实现是 x1.fun(x2) 这种形式,照样使用方法引用
如果理解了它们的规律,推而广之,可以试试抽象方法含有三个参数的情况。
准备工作:找一个三参数的函数式接口
1 | |
2 | public void test4() { |
3 | String s = "Hello World"; |
4 | Integer start = 0; |
5 | Integer length = 5; |
6 | String r1 = consume((o1, o2, o3) -> { |
7 | return o1.substring(o2, o3); |
8 | }, s, start, length); |
9 | System.out.println(r1); |
10 | |
11 | String r2 = consume(String::substring, s, start, length); |
12 | System.out.println(r2); |
13 | } |
14 | String consume(TripleFunction<String, Integer, Integer, String> tripleFunction, |
15 | String s, |
16 | Integer start, |
17 | Integer length) { |
18 | return tripleFunction.apply(s, start, length); |
19 | } |
20 | // 自定义三参数函数式接口 |
21 | |
22 | interface TripleFunction<T, U, E, R> { |
23 | R apply(T t, U u, E e); |
24 | } |
这里 函数式接口 TripleFunction 的抽象方法 apply(T t, U u, E e)中的参数 t, u, e 与 s.substring(start, length) 中的 s,start, length 对应
小结:
设函数式接口抽象方法
abstractFun(n1, n2, n3, ..., nn)**
*有方法fun(n1, n2, n3, ..., nn)或n1.fun(n2, n3, ..., nn)* 实现了 lambda 体的代码功能
**就可使用方法引用ClassName::fun
构造器引用
用法:
1 | |
2 | Function<Integer, MyClass> fun1 = (i) -> new MyClass(i); |
3 | // 使用构造器引用 |
4 | Function<Integer, MyClass> fun2 = MyClass::new; |
5 | |
6 | Function<Integer, Integer[]> fun3 = (n) -> new Integer[n]; |
7 | // 使用构造器引用 |
8 | Function<Integer, Integer[]> fun4 = Integer[]::new; |
函数式接口方法参数列表,必须和构造函数参数列表一致 (和方法应用的第一种情况相同)