【问题标题】:What is meant by lambda target type and target type context in Java?Java中的lambda目标类型和目标类型上下文是什么意思?
【发布时间】:2019-03-17 09:33:03
【问题描述】:

我正在阅读 Herbert Schildt 的“Java: The Complete Reference”中关于 lambdas 的一章,其中有很多关于“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&lt;String&gt;?那么这里的 lambda 上下文是什么?

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

谢谢。

【问题讨论】:

  • 我差不多十年前写了这个答案,显然在将 lambdas 引入 Java 之前:stackoverflow.com/a/4229898/2988 我刚刚添加了一段关于现代 Java 及其与 8 之前 Java 的比较的段落。在 pre-8 部分中,它包含对所有问题的解释,在 post-11 部分中,它解释了目标类型的 lambdas 如何解决其中一个问题。如果不了解为什么我们首先拥有它们,即它们应该解决什么问题,我认为你不会理解目标类型的 lambda。

标签: java lambda


【解决方案1】:

“目标”的定义之一(取自here)是:

您打算达到的结果或情况。

你可以说一个 lambda 表达式想要实现的结果是实现一些函数式接口。因此,函数式接口可以被视为该 lambda 表达式的目标,而函数式接口的类型就是目标类型。

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

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

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

(int n) -> n % 2 == 0

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

MyInterface<String> myInt = () -> { return "123"; }

目标类型是MyInterface&lt;String&gt;

【讨论】:

    【解决方案2】:

    我决定阅读更多有关 lamdas 的内容,并找到一本 Kishori Shiran 的优秀书籍“Java 8 语言特性入门:Lambda 表达式、内部类、线程、I/O、集合和流”。

    我只引用几段:

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

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

    (String str) -&gt; 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 表达式是否符合 StringToIntMapper 接口中 map() 方法的声明;最后推断出lambda表达式的类型是StringToIntMapper接口类型。

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

    独立表达式是一种表达式,其类型可以由表达式确定,而无需知道其使用的上下文。多边形表达式是在不同上下文中具有不同类型的表达式。编译器确定表达式的类型。允许使用 poly 表达式的上下文称为 poly 上下文。 Java 中的所有 lambda 表达式都是 poly 表达式。您必须在上下文中使用它才能知道它的类型。 Poly 表达式在 Java 8 和 lambda 表达式之前存在于 Java 中。例如,表达式new ArrayList&lt;&gt;() 是一个多边形表达式。你 除非您提供其使用的上下文,否则无法分辨其类型。

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

    T t = <LambdaExpression>;
    

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

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

    【讨论】:

      【解决方案3】:

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

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

      () -> "123";
      

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

      现在,什么函数接口可以是 lambda 表达式的类型是从上下文中挑选出来的。这就是 lambda 表达式的“目标类型”有意义的地方。

      考虑以下示例:

      示例 1:

      void printString(Supplier<String> supplier) {
          System.out.println(supplier.get());
      }
      

      你可以调用它

      printString(() -> "123");
      

      在这种情况下,您的意思是() -&gt; "123" 的类型为Supplier&lt;String&gt;。这是() -&gt; "123" 在此上下文中的目标类型。

      示例 2:

      MyInterface<String> myInt = () -> "123";
      

      如您所见,使用了相同的 lambda 表达式,但它的目标类型现在是MyInterface&lt;String&gt;

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

      【讨论】:

        猜你喜欢
        • 2016-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-30
        • 1970-01-01
        • 2015-10-19
        • 2015-02-20
        • 1970-01-01
        相关资源
        最近更新 更多