Java|Lambda、函数式接口使用

#Java [字体 ··]

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 包下的一组抽象接口。

image.png

接口支持 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());

常用函数式接口

接口参数返回类型抽象方法描述其他方法
Runablevoidrun作为无参或返回值的动作执行
Consumer
BiComsumer<T,U>
Function<T,R>
BiFunction<T,U,R>
UnaryOperator<T>
BinaryOperator<T>
Predicate<T>
BiPredicate<T,U>


博客没有评论系统,可以通过 邮件 评论和交流。 Top↑