Lambda 是 JDK8 的新特性,Lambda 让 Java 支持了函数式的编程,如 JS 那样可以在参数中使用函数(JS 中函数是对象)。
这些特性是 Java8 的一些特性,使用这些特性能够简化我们的代码,让代码更清晰。
Lambda 使用示例
在支持回调的方法中使用,典型的 Runnable 接口的 run(),Comparator 的 compare()。
1 // 使用Lambda执行一个线程的run方法
2 new Thread(() -> {
3 try {
4 while (true) {
5 System.out.println("hello elltor!");
6 Thread.sleep(1000);
7 }
8 } catch (InterruptedException e) {
9 e.printStackTrace();
10 }
11 }).start();
12
13 // 自定义compare实现
14 Arrays.sort(strs,(str1,str2)->{
15 // 根据首字母ascii差值判断大小
16 char c = str1.charAt(0);
17 char c2 = str2.charAt(0);
18 return c-c2;
19 });
20
21 for (String str : strs) {
22 System.out.println(str);
23 }
24
25 // 自定义compare实现2
26 Comparator<String> comparator = (str1,str2)->{
27 char c = str1.charAt(0);
28 char c2 = str2.charAt(0);
29 return c-c2;
30 };
31 // 使用
32 Arrays.sort(strs,comparator);
Lambda 语法与注意事项
首先,如果要使用 lambda,就要实现函数式接口,如 Runnable 的 run 方法,稍后讲解函数式接口。
Lambda 语法。
Lambda 的标志是 “->”, 前面接收参数的括号和后面的方法题在特殊的情况下都可以省略。
1// 基本语法
2() ->{}
3
4// 省略括号,当且仅当只有一个参数时才允许这么做
5ActionListener listener = e -> {
6 System.out.println(this.aa);
7 System.out.println("监听得到事件");
8};
9
10// 省略方法体,当且仅当方法体只有只有一个return+返回值时才允许这么做
11list.add(1);
12list.add(2);
13list.add(3);
14list.add(4);
15System.out.println(list.toString());
16list.removeIf(val -> val <= 2); // 即省略了括号也省略了方法体
17System.out.println(list.toString());
使用 Lambda 的注意事项:
- Lambda 方法体中不具有 this,它包含的 this 是包含 Lambda 的方法的 this
- Lambda 方法体中引用的变量不能改变,不能在方法体外边改变或者里面改变,这是 Lambda 的语法规定。从语言层面来讲,如果可以改变那将带来线程安全性问题。
- Lambda 函数回调有固定的类型,这些类型根据函数式接口确定,Runnable 的 run 接收的是一个无参的 Lambda,而 Predicate 接收的是一个参数 Lambda
函数式接口
实现 Lambda 依赖的是函数式接口,函数式接口规定了传入什么样格式的 Lambda(有无参数或返回值,参数个数等)。函数式接口(Function Interface)是 java.util.function 包下的一组抽象接口。
接口支持 Lambda 的原理——一方法参数使用了函数接口,二方法调用函数式接口的方法。
先看 ArrayList 的 removeIf 的源码。
1 // ArrayList的removeIf的源码
2 default boolean removeIf(Predicate<? super E> filter) {
3 Objects.requireNonNull(filter);
4 boolean removed = false;
5 final Iterator<E> each = iterator();
6 while (each.hasNext()) {
7 if (filter.test(each.next())) {
8 each.remove();
9 removed = true;
10 }
11 }
12 return removed;
13 }
14
15// Predicate 核心源码
16@FunctionalInterface
17public interface Predicate<T> {
18 /**
19 * Evaluates this predicate on the given argument.
20 */
21 boolean test(T t);
22}
方法引用
方法引用的语法:
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
示例
1List<Integer> list = Arrays.asList(1,2,3,4,5);
2list.forEach(System.out::println);
3String[] strings = new String[]{"c","b","a"};
4
5Arrays.sort(strings,String::compareToIgnoreCase);
6System.out.println(Arrays.toString(strings));
方法的引用是传递一个方法的引用过去,它等价于 Lambda 的 x->System.out.println(x).
当被传入的方法有重载时,编译器会根据上下文推断应该调用的方法是什么类型,如 Math::max 方法有两个,传入 double 和传入 int 的,选择哪一个取决于 Math::max 函数式接口的具体参数, 关键在多态运行时的匹配。
1public class RefTest {
2 public static void main(String[] args) {
3 RefTest.printMax(2,3,Math::max);
4 }
5
6 static void printMax(int a, int b, BiFunction<Integer,Integer, Integer> accept){
7 // apply将调用Math.max()方法,并返回比较结果
8 System.out.println("max = " + accept.apply(a, b));
9 }
10}
11// output
12max = 3
构造器引用
构造器引用示例
1List<String> names = Arrays.asList("zhangsan","lisi","wanger");
2names.stream().map(Person::new).collect(Collectors.toList());
常用函数式接口
接口 | 参数 | 返回类型 | 抽象方法 | 描述 | 其他方法 |
---|---|---|---|---|---|
Runable | 无 | void | run | 作为无参或返回值的动作执行 | 无 |
Consumer |
|||||
BiComsumer<T,U> | |||||
Function<T,R> | |||||
BiFunction<T,U,R> | |||||
UnaryOperator<T> | |||||
BinaryOperator<T> | |||||
Predicate<T> | |||||
BiPredicate<T,U> |