一、简介
Lambda 表达式的加入,使得 Java 拥有了函数式编程的能力。在其它语言中,Lambda 表达式的类型是一个函数;但在 Java 中,Lambda 表达式被表示为对象,因此它们必须绑定到被称为功能接口的特定对象类型。
**前提条件:
- 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的 Runnable 、 Comparator 接口还是自定义的接口,只有当接口中的抽象方法存在且唯一 时,才可以使用Lambda。
- 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
- 有且仅有一个抽象方法的接口,称为“函数式接口”# 函数式。
Java 中的 Lambda 表达式通常使用语法是 (argument) -> (body)
,比如:
(arg1, arg2...) -> { body }
(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 表达式,我们已经看到代码可以变得非常简洁。例如,要创建一个比较器,以下语法就足够了
但是,我们可以使上面的代码更具表现力和可读性吗?我们来看一下:
Comparator c = Comparator.comparing(Person::getAge);
再看看下面的常见的用法:
例如 调用PrintStream类的实例方法:
Consumer<String> consumer = (s) -> System.out.println(s);
等同于:
Consumer<String> consumer = System.out::println;
例如 Integer的静态方法:
Function<String, Integer> stringToInteger = (String s) -> Integer.parseInt(s);
等同于:
Function<String, Integer> stringToInteger = Integer::parseInt;
例如:
BiPredicate<List<String>, String> contains = (list, element) -> list.contains(element);
等同于:
BiPredicate<List<String>, String> contains = List::contains;
常见的用法:
**
// 表达式:
person -> person.getAge();
// 可以替换成
Person::getAge
// 表达式
() -> new HashMap<>();
// 可以替换成
HashMap::new
注意:
- Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保存一致
- 若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
3.2 使用方式
双冒号(::
)操作符是 Java 中的方法引用。 当们使用一个方法的引用时,目标引用放在 ::
之前,目标引用提供的方法名称放在 ::
之后,即 目标引用::方法
。比如:
Person::getAge;
在 Person
类中定义的方法 getAge
的方法引用。
然后我们可以使用 Function
对象进行操作:
// 获取 getAge 方法的 Function 对象
Function<Person, Integer> getAge = Person::getAge;
// 传参数调用 getAge 方法
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的改善)