Liuqichun's Blog

Java|Lambda、函数式接口使用

Lambda 是 JDK 8 的新特性,让 Java 支持了函数式编程,如 JS 那样可以在参数中使用函数(JS 中函数是对象)。

这些是 Java 8 的特性,能够简化代码,让代码更清晰。

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 依赖的是函数式接口,函数式接口规定了传入什么样格式的 Lambda(有无参数或返回值,参数个数等)。函数式接口(Functional 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}

方法引用

方法引用的语法:

示例

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());

常用函数式接口

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

<< Previous Post

|

Next Post >>

#Java