Java中的lambda目标类型和目标类型上下文是什么意思?

Joh*_*son 6 java lambda

我正在阅读赫伯特·希尔尔德(Herbert Schildt)的“ Java:完整参考”中有关lambda的一章,其中有很多参考“ lambda目标类型”和“目标类型上下文”:

功能接口定义lambda表达式的目标类型。这是一个关键点:lambda表达式只能在指定其目标类型的上下文中使用。

要么:

如前所述,lambda表达式不是单独执行的。相反,它形成了由指定其目标类型的功能接口定义的抽象方法的实现。结果,只能在定义目标类型的上下文中指定lambda表达式。当将lambda表达式分配给功能接口引用时,将创建这些上下文之一。其他目标类型上下文 包括变量初始化,返回语句和方法参数,仅举几例。

完后还有:

与lambda表达式关联的功能接口可以是通用的。在这种情况下,lambda表达式的目标类型部分由声明函数接口引用时指定的一个或多个类型实参确定。

有人可以帮我了解lambda目标类型的含义吗?

例如,在(int n) -> n % 2 == 0int拉姆达的目标类型?

或在:

interface MyInterface<T> {
    T func();
}

MyInterface<String> myInt = () -> { return "123"; }
Run Code Online (Sandbox Code Playgroud)

Lambda的目标类型是什么?是String还是MyInterface<String>?lambda的背景是什么?

我阅读了有关该主题的SO的几篇文章,但仍然无法完全理解这些概念。

谢谢。

Era*_*ran 7

“目标”(从此处获取)的定义之一是:

您打算实现的结果或情况。

可以说lambda表达式打算实现的结果是实现一些功能接口。因此,该功能接口可以看作是该lambda表达式的目标,而功能接口的类型就是目标类型。

因此,目标类型是由lambda表达式实现的功能接口的类型。

可以根据使用lambda表达式的上下文来推断目标类型:

  1. 如果将lambda表达式分配给功能接口引用变量,则该变量的类型为目标类型。
  2. 如果lambda表达式由某些方法返回,则该方法的返回类型为目标类型。
  3. 如果将lambda表达式作为参数传递给方法,则该方法期望的相应参数的类型为目标类型。

(int n) -> n % 2 == 0
Run Code Online (Sandbox Code Playgroud)

目标类型未知。如果将此表达式分配给某个功能接口引用,则将是目标类型。

MyInterface<String> myInt = () -> { return "123"; }
Run Code Online (Sandbox Code Playgroud)

目标类型为MyInterface<String>


Joh*_*son 7

我决定多读一点有关 lamda 的内容,并找到了 Kishori Shiran 写的一本优秀的书“Beginning Java 8 Language Features: Lambda Expressions, Inner Classes, Threads, I/O, Collections and Streams”。

我只引用几段:

Java 中的每个表达式都有一个类型;lambda 表达式也是如此。lambda 表达式的类型是函数接口类型。当调用函数式接口的抽象方法时,将执行 lambda 表达式的主体。

考虑采用 String 参数并返回其长度的 lambda 表达式:

(String str) -> str.length()

这个 lambda 表达式的类型是什么?答案是我们不知道。通过查看 lambda 表达式,您只能说它接受一个 String 参数并返回一个 int,即 String 的长度。它的类型可以是任何具有抽象方法的函数接口类型,该抽象方法接受 String 作为参数并返回 int。以下是此类功能接口的示例:

@FunctionalInterface
interface StringToIntMapper {
    int map(String str);
}
Run Code Online (Sandbox Code Playgroud)

当lambda 表达式StringToIntMapper出现在赋值语句中时,它表示函数式接口的一个实例,如下所示:

StringToIntMapper mapper = (String str) -> str.length();
Run Code Online (Sandbox Code Playgroud)

在此语句中,编译器发现赋值运算符的右侧是 lambda 表达式。为了推断其类型,它会查看需要接口实例的赋值运算符的左侧StringToIntMappermap()它验证 lambda 表达式是否符合接口中方法的声明StringToIntMapper;最后,它推断 lambda 表达式的类型是StringToIntMapper接口类型。

该 lambda 表达式可能具有不同的函数接口类型,具体取决于使用它的上下文。Java中有两种类型的表达式 - 独立表达式和聚合表达式

独立表达式是可以在不知道其使用上下文的情况下由表达式确定其类型的表达式。多元表达式是在不同上下文中具有不同类型的表达式。编译器确定表达式的类型。允许使用多表达式的上下文称为多上下文。Java 中的所有 lambda 表达式都是聚合表达式。您必须在上下文中使用它才能知道它的类型。Poly 表达式早于 Java 8 和 lambda 表达式就已存在于 Java 中。例如,该表达式new ArrayList<>()是一个poly表达式。除非您提供其使用上下文,否则您无法判断其类型。

编译器推断 lambda 表达式的类型。使用 lambda 表达式的上下文需要一个类型,称为目标类型。从上下文推断 lambda 表达式类型的过程称为目标类型。考虑以下赋值语句的伪代码,其中为类型变量T分配了 lambda 表达式:

T t = <LambdaExpression>;
Run Code Online (Sandbox Code Playgroud)

此上下文中 lambda 表达式的目标类型是T。编译器使用以下规则来确定 is<LambdaExpression>赋值是否与其目标类型兼容T

  • T必须是函数式接口类型。
  • lambda 表达式与 的抽象方法具有相同数量和类型的参数T。对于隐式 lambda 表达式,编译器将从 的抽象方法中推断出参数的类型T
  • lambda 表达式主体返回值的类型与 的抽象方法的返回类型赋值兼容T
  • 如果 lambda 表达式的主体抛出任何已检查异常,则这些异常必须与 的抽象方法的声明的 throws 子句兼容T。如果目标类型的方法不包含 throws 子句,则从 lambda 表达式主体抛出已检查异常是一个编译时错误。


ern*_*t_k 5

您应该将“目标类型”理解为(打算)使用该功能的功能接口。

想一想:这个 lambda 表达式应该是什么以及如何使用它?

() -> "123";
Run Code Online (Sandbox Code Playgroud)

正如书中所指出的,这个表达不能单独使用。它需要与功能接口相关联。

现在,什么函数接口可以是 lambda 表达式的类型是从上下文中提取的。这就是 lambda 表达式的“目标类型”的意义所在。

考虑以下示例:

示例 1:

void printString(Supplier<String> supplier) {
    System.out.println(supplier.get());
}
Run Code Online (Sandbox Code Playgroud)

你可以用

printString(() -> "123");
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你的意思的类型() -> "123"Supplier<String>。这就是() -> "123"此上下文中的目标类型。

示例 2:

MyInterface<String> myInt = () -> "123";
Run Code Online (Sandbox Code Playgroud)

如您所见,使用了相同的 lambda 表达式,但其目标类型现在是MyInterface<String>.

同样,您可以声明另一个具有相同签名的函数式接口,MyInterface.func()并为其分配完全相同的 lambda 表达式。目标类型在这些不同的环境中会发生变化。