一、简介

Lambda 表达式的加入,使得 Java 拥有了函数式编程的能力。在其它语言中,Lambda 表达式的类型是一个函数;但在 Java 中,Lambda 表达式被表示为对象,因此它们必须绑定到被称为功能接口的特定对象类型。

**前提条件:

  • 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一 时,才可以使用Lambda。
  • 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
  • 有且仅有一个抽象方法的接口,称为“函数式接口”# 函数式。

Java 中的 Lambda 表达式通常使用语法是 (argument) -> (body),比如:

  1. (arg1, arg2...) -> { body }
  2. (type1 arg1, type2 arg2...) -> { body }

以下是 Lambda 表达式的一些示例:

二、 Lambda 表达式的结构

Lambda 表达式的结构:

  • 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如 (int a) 与刚才相同 (a)
  • 空括号用于表示一组空的参数。例如 () -> 42
  • 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a
  • 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
  • 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号
  • 需要注意的是,要省略的话就必须一起的进行省略,不能有的省略,有的不省略,这样就会出错

**

三、方法引用

3.1 常见的形式

方法引用是用来直接访问类或者实例已经存在的方法或构造方法,提供了一种引用而不执行方法的方式。是一种更简洁更易懂的Lambda表达式,当Lambda表达式中只是执行一个方法调用时,直接使用方法引用的形式可读性更高一些。 方法引用使用 “ :: ” 操作符来表示,左边是类名或实例名,右边是方法名。 (注意:方法引用::右边的方法名是不需要加()的,例:User::getName)

方法引用的几种形式:
:::info

  • 类 :: 静态方法
  • 类 :: 实例方法
  • 对象 :: 实例方法 :::

使用 Lambda 表达式,我们已经看到代码可以变得非常简洁。例如,要创建一个比较器,以下语法就足够了
但是,我们可以使上面的代码更具表现力和可读性吗?我们来看一下:

  1. Comparator c = Comparator.comparing(Person::getAge);

再看看下面的常见的用法:

  1. 例如 调用PrintStream类的实例方法:
  2. Consumer<String> consumer = (s) -> System.out.println(s);
  3. 等同于:
  4. Consumer<String> consumer = System.out::println;
  5. 例如 Integer的静态方法:
  6. Function<String, Integer> stringToInteger = (String s) -> Integer.parseInt(s);
  7. 等同于:
  8. Function<String, Integer> stringToInteger = Integer::parseInt;
  9. 例如:
  10. BiPredicate<List<String>, String> contains = (list, element) -> list.contains(element);
  11. 等同于:
  12. BiPredicate<List<String>, String> contains = List::contains;

常见的用法:
**

  1. // 表达式:
  2. person -> person.getAge();
  3. // 可以替换成
  4. Person::getAge
  5. // 表达式
  6. () -> new HashMap<>();
  7. // 可以替换成
  8. HashMap::new

注意:

  • Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保存一致
  • 若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method

3.2 使用方式

双冒号(::)操作符是 Java 中的方法引用。 当们使用一个方法的引用时,目标引用放在 :: 之前,目标引用提供的方法名称放在 :: 之后,即 目标引用::方法。比如:

  1. Person::getAge;

Person 类中定义的方法 getAge 的方法引用。
然后我们可以使用 Function 对象进行操作:

  1. // 获取 getAge 方法的 Function 对象
  2. Function<Person, Integer> getAge = Person::getAge;
  3. // 传参数调用 getAge 方法
  4. Integer age = getAge.apply(p);

我们引用 getAge,然后将其应用于正确的参数。

目标引用的参数类型是 Function<T,R>T 表示传入类型,R 表示返回类型。比如,表达式 person -> person.getAge();,传入参数是 person,返回值是 person.getAge(),那么方法引用 Person::getAge 就对应着 Function<Person,Integer> 类型。

四、Lambda是怎么实现的?

研究了半天Lambda怎么写,可是它的原理是什么?我们简单看个例子,看看真相到底是什么:

自定义的接口

参考文章

https://juejin.im/post/5d9ab5dae51d4578453274ba

https://juejin.im/post/5ab116875188255561411b8a#heading-29

https://juejin.im/post/5d2d15825188253d7201d297#heading-5 (java8的实现)

https://juejin.im/post/5ce4a049e51d45109725fdc9(if else的改善)