不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:php-note.com  发布于:2022-05-03 16:53  分类:Java基础  编辑

一、概念简述

  • Lambda 表达式(lambda expression)是一种表达式语法,注意在Java里面,它不是对象。以下均是lambda表达式
() -> "haha"
a -> "hehe"

Java中,类名::方法名对象::方法名 也都是 lambda 表达式

  • 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

二、函数式接口 与 Lambda表达式 的关系

  1. lambda表达式不能直接使用,在使用之前必须初始化
  2. lambda表达式必须借助函数式接口来初始化;
  3. lambda表达式初始化之前什么都不是,初始化之后是一个函数式接口的实例对象;

2.1 lambda表达式 不能直接使用,在使用之前必须初始化

下面三个lambda表达式 只是一种语法,你无法确定它们到底是个什么东西。

() -> {}
() -> "gaga";
String::new;

2.2 lambda表达式 必须借助 函数式接口 来初始化

以 () -> "gaga" 为例,它是一个lambda表达式,必须初始化才能使用,要想初始化,必须要借助具体的函数式接口。

下面是这个lambda表达式的初始化。

// 被 Supplier 初始化之后,变成一个 Supplier 实例对象
Supplier<String> supplier = () -> "gaga";

// 被 Runnable 初始化之后,变成一个 Runnable 实例对象
Runnable supplier = () -> "gaga";
// 当然你也可以自定义一个韩式式

甚至你可以自定义一个函数式接口,如下

@FunctionalInterface
interface Haha {
  Object hehe();
}

// 被 Haha 初始化之后,变成一个 Haha 实例对象
Haha supplier = () -> "gaga";

2.3 lambda表达式 初始化之前什么都不是,初始化之后是一个 函数式接口 的实例对象

  1. 函数式接口实例对象是一个对象,因此是一个Object,拥有 Object 的一切特性。
  2. lambda 表达式 在初始化之前只是一种语法, 它不是对象,也不能被引用。
// 被 Supplier 初始化之后, 变成一个 Supplier 实例对象
Supplier<String> supplier = () -> "gaga";

// 初始化之后是一个函数式接口实例对象, 因此可以被 Object 引用
Object o = arr;

// 但是 你不能直接使用 Object 引用它, 因为它还未经过初始化, 此时还不是对象.
Object f = () -> "gaga"; // ERROR

2.4 此外 函数式接口 还有一些有趣的地方

lambda表达式 在被函数式接口初始化后成为一个对象。

在Java 中,类名::方法名 和 对象::方法名 也都是 lambda 表达式

因此,对象::方法名 还能被函数式接口初始化。

示例

// supplier1 是一个函数式接口实例对象
Supplier<String> supplier1 = () -> "gaga"

// supplier1 是一个对象,因此 supplier1::get 是一个lambda表达式,既然是 lambda 表达式 那么能被函数式接口初始化
Supplier<String> supplier2 = supplier1::get;

// 我要连续初始化 100次,每次初始化都相当于创建了一个新的实例对象
for (int i = 0; i < 100; i ++) {
   supplier2 = supplier2::get
}

那么,这个有什么用呢?

例如,下面是一个函数式接口Haha和一个实例Supplier,它们的结构大同小异。

@FunctionalInterface
interface Haha {
  Object hehe();
}

Supplier<String> supplier = () -> "gaga"

你需要一个 Haha 接口实例,但是现在你只有一个 Supplier<String> 实例,此时你就可以这样转换

Haha haha = supplier::get;

2.5 数组的初始化(对比lambda表达式初始化)

如果你对lambda的初始化还不了解,那就请参照下数组的初始化,lambda表达式和数组的初始化很像。

  1. {3, 4, 5, 6} 是一种表达数组的语法,它不能直接使用,在使用之前必须初始化
  2. {3, 4, 5, 6} 必须借助具体的类型来初始化。
  3. {3, 4, 5, 6} 初始化之后是一个数组的实例对象。

int[] 是一种具体的类型, int[].class 是有意义的

// 1. 这个是数组的初始化,初始化可不仅仅是一个赋值引用的操作。
int[] arr = {3, 4, 5, 6};

// 被 byte[] 初始化后是一个byte数组,被 Integer[] 初始化之后是一个 Integer 数组
byte[] arr2 = {3, 4, 5, 6};
Integer[] arr3 = {3, 4, 5, 6};

// arr 初始化之后是一个对象,因此 arr 在初始化之后可以被 Object 引用
Object o = arr;

// 但是 你不能直接使用 Object 引用它,因为它还未经过初始化,此时还不是对象。
Object f = {3, 4, 5, 6}; // ERROR

三、总结

Lambda 表达式就是函数式接口(FunctionalInterface)实现的快捷方式,它相当于函数式接口实现的实例,因为在方法中可以使用 Object作为参数,所以把 Lambda 表达式作为方法的参数也是可以的。

 

 

参考:

函数式接口 与 lambda 表达式 的关系

函数式接口和lambad表达式有什么关系?

Lambda表达式与函数式接口的关系