我正在阅读赫伯特·希尔德(Herbert Schildt)的“ Java:完整参考”中有关lambda的一章,其中有很多参考有关“ lambda目标类型”和“目标类型上下文”:
功能界面定义了广告的目标类型 lambda表达式。这是一个关键点:lambda表达式只能使用 在指定其目标类型的上下文中。
或者:
如前所述,lambda表达式不是单独执行的。 相反,它形成了由 指定其目标类型的功能界面。结果,一个lambda 表达式只能在目标类型为 定义。当lambda表达式为 分配给功能接口参考。其他目标类型上下文 包括变量初始化,返回语句和方法参数,以 列举几个。
另一个:
与lambda表达式关联的功能接口可以是通用的。在 在这种情况下,lambda表达式的目标类型部分由一个或多个函数接口时指定的类型实参确定 引用已声明。
有人可以帮我理解lambda目标类型的含义吗?
例如,在(int n) -> n % 2 == 0
中int
是lambda的目标类型?
或在:
interface MyInterface<T> {
T func();
}
MyInterface<String> myInt = () -> { return "123"; }
lambda的目标类型是什么?是String
还是MyInterface<String>
?那么lambda的背景是什么?
我阅读了有关该主题的几篇SO文章,但仍然无法完全理解这些概念。
谢谢。
答案 0 :(得分:6)
“目标”的定义之一(取自here)是:
您打算实现的结果或情况。
您可以说lambda表达式打算实现的结果是实现一些功能接口。因此,可以将功能接口视为该lambda表达式的目标,而功能接口的类型就是目标类型。
因此,目标类型是由lambda表达式实现的功能接口的类型。
可以根据使用lambda表达式的上下文来推断目标类型:
在
(int n) -> n % 2 == 0
目标类型未知。如果将此表达式分配给某个功能接口引用,则将是目标类型。
在
MyInterface<String> myInt = () -> { return "123"; }
目标类型为MyInterface<String>
。
答案 1 :(得分:3)
您应该将“目标类型”理解为(打算使用)该功能的功能接口。
考虑一下:这个lambda表达式应该是什么以及如何使用?
() -> "123";
正如书中指出的那样,该表达式不能单独使用。它需要与功能接口相关联。
现在,从上下文中获取可以用作lambda表达式类型的功能接口。这就是lambda表达式的“目标类型”的含义。
考虑以下示例:
示例1:
void printString(Supplier<String> supplier) {
System.out.println(supplier.get());
}
您可以通过以下方式致电
printString(() -> "123");
在这种情况下,您的意思是() -> "123"
的类型为Supplier<String>
。在这种情况下,这就是() -> "123"
的目标类型。
示例2:
MyInterface<String> myInt = () -> "123";
如您所见,使用了相同的lambda表达式,但其目标类型现在为MyInterface<String>
。
同样,您可以声明另一个具有与MyInterface.func()
相同签名的功能接口,并为其分配完全相同的lambda表达式。目标类型在这些不同的上下文中发生变化。
答案 2 :(得分:0)
我决定阅读更多有关lamda的内容,并找到了Kishori Shiran撰写的一本很棒的书“ Java 8语言特性:Lambda表达式,内部类,线程,I / O,集合和流”。
我只引用几段:
Java中的每个表达式都有一个类型; lambda表达式也是如此。 Lambda表达式的类型是功能接口类型。调用功能接口的抽象方法时,将执行lambda表达式的主体。
考虑使用String参数并返回其长度的lambda表达式:
(String str) -> str.length()
此lambda表达式的类型是什么?答案是我们不知道。通过查看lambda表达式,您可以说的是,它接受一个String参数并返回一个int,它是String的长度。它的类型可以是具有抽象方法的任何函数接口类型,该抽象方法将String作为参数并返回一个int。以下是此类功能界面的示例:
@FunctionalInterface
interface StringToIntMapper {
int map(String str);
}
当lambda表达式出现在赋值语句中时,它代表
StringToIntMapper
功能接口的实例,如下所示:
StringToIntMapper mapper = (String str) -> str.length();
在此语句中,编译器发现赋值运算符的右侧是lambda表达式。为了推断其类型,它查看赋值运算符的左侧,该运算符期望一个
StringToIntMapper
接口的实例;它验证lambda表达式是否符合map()
接口中StringToIntMapper
方法的声明;最后,它推断出lambda表达式的类型是StringToIntMapper
接口类型。此lambda表达式可能具有不同的功能接口类型,这取决于使用它的上下文。 Java中有两种类型的表达式-独立表达式和多边形表达式
独立表达式是一种表达式,其类型可以在不了解其使用上下文的情况下由该表达式确定。多边形表达式是在不同上下文中具有不同类型的表达式。编译器确定表达式的类型。允许使用多边形表达式的上下文称为多边形上下文。 Java中的所有lambda表达式都是多边形表达式。您必须在上下文中使用它来了解其类型。在Java 8和lambda表达式之前,poly表达式存在于Java中。例如,表达式
new ArrayList<>()
是一个多边形表达式。您 除非您提供其使用的上下文,否则无法说出它的类型。编译器推断lambda表达式的类型。使用lambda表达式的上下文需要一个类型,称为目标类型。根据上下文推断lambda表达式的类型的过程称为目标类型。考虑以下赋值语句的伪代码,其中为类型
T
的变量分配了lambda表达式:
T t = <LambdaExpression>;
在此上下文中,lambda表达式的目标类型为
T
。编译器使用以下规则来确定<LambdaExpression>
是否与目标类型T
兼容:
T
必须是功能接口类型。- lambda表达式的参数数量和类型与
T
的抽象方法相同。对于隐式lambda表达式,编译器将从T
的抽象方法中推断参数的类型。- lambda表达式主体返回的值的类型与
T
抽象方法的返回类型兼容。- 如果lambda表达式的主体抛出任何已检查的异常,则这些异常必须与
T
抽象方法的声明的throws子句兼容。如果其目标类型的方法不包含throws子句,则从lambda表达式的主体中引发检查的异常是编译时错误。