Java8之lambda表达式

简介

Lambda 表达式是 Java 1.8 跟 Stream 机制一同推出的。Lambda 表达式极大地减少了代码量,增加了代码的可读性。
引入 Lambda 表达式之后,Java 开始支持把函数作为参数传递

前置条件

使用 Lambda 表达式的前置条件,作为参数的接口必须是函数式接口

  1. 首先类型必须是接口 interface,不能是类 class。比如,抽象类就不可以。
  2. 函数式接口有且仅有一个未被覆写的抽象方法

举例:

  • Object 中方法不算
// MyRunnable 仍然算是一个“函数式接口”
public interface MyRunnable extends Runnable {
    String toString();
    boolean equals(Object obj);
    int hashCode();
}
  • 接口中的 default 方法不算
// MyRunnable2 仍然算是一个“函数式接口”
public interface MyRunnable2 extends Runnable {
    default void run2() {}
}

FunctionalInterface 注解

@FunctionalInterface 可以帮助我们在编译期识别出一个接口是否是“函数式接口”:

Not Functional Interface

参数的传递

假如我们有一个如下含义的“函数式接口”:

@FunctionalInterface
public interface Formatter {
    void format(String name, int age);
}

我们可以构造一个测试:

public class LambdaTest {

    public static void main(String[] args) {
        print((String name, int age)-> System.out.println(String.format("name:%s age:%d", name, age)), "ziyu", 18);
    }

    public static void print(Formatter formatter, String name, int age) {
        formatter.format(name, age);
    }
}

多个参数

当有多个参数时,可以选择省略所有参数类型声明:
lambda-params
注意:
不能省略一部分保留一部分。(String name, age) -> System.out.println(name); 这是不合法的!

单个参数

lambda-param
当只有一个参数时,除了可以省略参数类型,还可以进一步省略掉括号。

编写方式

没有返回值

Runnable 就是一个常用的“函数式接口”,它的抽象方法 run() “没有返回值”, 刚好适合用于此处的演示。测试例子如下:

public class LambdaTest2 {
    public static void main(String[] args) {
        runIt(()->{
            System.out.println("123");
        });
    }

    static void runIt(Runnable runnable) {
        new Thread(runnable).start();
    }
}

return-void

  • 如果写成多行表达式,那么需要 {} 来表示代码块,且每一行代码结束时需要书写 ; 表示语句的结束。
  • 如果代码块中只有一条语句,那么可以通过省略 {}; 来简写为单行表达式

有返回值

我们定义一个 IdFactory 接口来做演示:

public interface IdFactory {
    String generateId();
}

我们的示例代码如下:

import java.util.UUID;

public class LambdaTest3 {

    public static void main(String[] args) {
        String name = getId(()-> UUID.randomUUID() + "");
        System.out.println(name);
    }

    static String getId(IdFactory factory) {
        return factory.generateId();
    }

}

return-string

  • 如果写成多行表达式,除了需要 {} 来表示代码块,和每一行代码结束时需要书写 ; 表示语句的结束以外,还应该在需要返回值的方法用 return 来返回值。
  • 如果代码块中只有一条语句,那么可以通过省略 {}; 以及 return 来简写为单行表达式

方法引用

比如我们要写一段代码,用来打印出完整的加法表达式 a + b = c,并且要求根据 a 和 b 求出 c 作为函数返回值。
首先我们定一个“计算器”接口:

public interface Calculator {
    int compute(int a, int b);
}

接着我们写一个测试用例:

public class LambdaTest3 {

    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        // 这里不能用 (a, b), 那样的话会产生歧义,使得编译器报错
        runIt((x, y)->{
            System.out.print(x);
            System.out.print(" + ");
            System.out.print(y);
            System.out.print(" = ");
            System.out.println(x + y);
            return x + y;
        }, a, b);
    }

    static void runIt(Calculator calculator, int a, int b) {
        calculator.compute(a, b);
    }
}

静态方法引用

static-method-reference

  • 在 LambdaTest2 中定一个静态方法 sum
  • 使用静态方法引用 LambdaTest2::sum
  • 这样在运行静态方法 runIt 中的第一行 calculator.compute(a, b) 时,ab 会通过静态方法引用传递给静态方法 sum

通过这种方式,即简化了参数的传递,也把“多行表达式”简化为了“单行表达式”

成员方法引用

member-method-reference

  • new LambdaTest2()::sum: 先新建对象 LambdaTest2,并且通过该对象来使用成员变量引用
    小贴士:
    如果先声明变量 obj,再使用 obj::sum 也是合法的。
public static void main(String[] args) {
    int a = 10;
    int b = 20;
    LambdaTest2 obj = new LambdaTest2();
    runIt(obj::sum, a, b);
}

总结

  • Lambda表达式的前置条件:必须是“函数式接口”
  • 单个参数传递时,可以省略参数两端的括号。参数的类型可以一起省略。
  • 编写的方式主要包括单行表达式和多行表达式
  • 可以使用方法引用来把多行表达式写成单行表达式。方法引用又包括了静态方法引用和动态方法引用。
posted @ 2020-07-14 19:28  极客子羽  阅读(2106)  评论(0编辑  收藏  举报