【问题标题】:Does a method reference in Java 8 have a concrete type and if so, what is it? [duplicate]Java 8 中的方法引用是否具有具体类型,如果有,它是什么? [复制]
【发布时间】:2015-07-04 16:33:21
【问题描述】:

这个问题与another one 密切相关。但是,我觉得这个问题的公认答案并不那么确定。

那么,Java 8 中方法引用的类型是什么?下面是如何将方法引用“转换”(提升?)到java.util.function.Function 的一个小演示:

package java8.lambda;

import java.util.function.Function;

public class Question {
  public static final class Greeter {
    private final String salutation;

    public Greeter(final String salutation) {
      this.salutation = salutation;
    }

    public String makeGreetingFor(final String name) {
      return String.format("%s, %s!", salutation, name);
    }
  }

  public static void main(String[] args) {
    final Greeter helloGreeter = new Greeter("Hello");

    identity(helloGreeter::makeGreetingFor)
      .andThen(g -> "<<<" + g + ">>>")
      .apply("Joe");

    //Compilation error: Object is not a function interface
//    Function
//      .identity()
//      .apply(helloGreeter::makeGreetingFor)
//      .andThen(g -> "<<<" + g + ">>>")
//      .apply("Joe");

    Function
      .<Function<String,String>>identity()
      .apply(helloGreeter::makeGreetingFor)
      .andThen(g -> "<<<" + g + ">>>")
      .apply("Joe");

    //Compilation error: Cannot resolve method 'andThen(<lambda expression>)'
//    (helloGreeter::makeGreetingFor)
//      .andThen(g -> "<<<" + g + ">>>")
//      .apply("Joe");

//    java.lang.invoke.LambdaMetafactory ???
  }

  private static <I,O> Function<I,O> identity(final Function<I,O> fun1) {
    return fun1;
  }
}

那么,是否有一种不那么痛苦(更直接)的方式将方法引用转换为可以传递的编译/具体类型?

【问题讨论】:

  • Function&lt;String, String&gt; f = helloGreeter::makeGreetingFor; ?推断方法引用的类型。相同的方法引用可以用作消费者、函数或其他功能接口,具体取决于上下文。
  • 如果您觉得某个问题没有得到足够好的回答,您应该在最初的问题上提供奖励,而不是再次询问。您的问题与之前的问题有何不同,或者我可以将其作为重复项关闭吗?
  • 我想,本质上,它们是相同的。虽然这里的答案似乎更具体。我会自己关闭它。
  • 这个问题掩盖了一个不正确的假设:所有表达式都具有内在(自下而上)类型。方法引用和 lambda 是 poly 表达式 的示例,表达式的类型取决于其上下文。方法 ref 或 lambda 的类型只是它被分配/转换到的类型(前提是该类型与相关的 lambda/方法 ref 兼容。)
  • @Andrey 你的惊喜是错的; Java 中的每个表达式确实 都有一个具体的编译时类型。这里的新内容是,对于某些表达式,该类型可能会受到表达式出现的上下文的影响(或需要来自该上下文的类型信息),例如赋值目标类型。 Lambda 和方法 ref 有一个类型,但这不是由表达式本身决定的,而是由外部类型信息(赋值或强制转换目标类型,当然要经过适用性检查)决定。

标签: java lambda functional-programming java-8 method-reference


【解决方案1】:

如果你只有一个方法引用helloGreeter::makeGreetingFor,它没有类型。

如果你想给方法引用一个类型而不分配它或将它作为参数传递(将它分配给参数),你可以强制转换它:

String greeting =
    ((Function<String, String>)helloGreeter::makeGreetingFor)
        .apply("Joe");

【讨论】:

    【解决方案2】:

    方法引用只是使用传递参数作为输入参数的函数的语法糖。因此,您可以这样分配它们:

    Runnable runnable = System.out::println;
    Consumer consumer = System.out::println;
    

    类型是推断出来的,取决于上下文。

    你的情况:

    Function<String, String> foo = helloGreeter::makeGreetingFor;
    

    它等于:

    Function<String, String> foo = s -> helloGreeter.makeGreetingFor(s);
    

    【讨论】:

      【解决方案3】:

      来自JLS, section 15.13.2, "Type of a Method Reference"

      方法引用表达式在赋值上下文中是兼容的, 调用上下文,或者如果 T 是一个目标类型 T 的强制转换上下文 功能接口类型(第 9.8 节)并且表达式与 从T派生的地面目标类型的函数类型。

      ...

      如果方法引用表达式与目标类型 T 兼容, 那么表达式的类型 U 是派生的地面目标类型 来自T。

      基本上,方法引用的类型是上下文所期望的。独立于上下文,方法引用并没有真正的类型。无法传递“原始”方法引用,然后将其转换为 Function 或 Consumer 或其他任何内容。

      【讨论】:

        【解决方案4】:

        首先,方法引用“是紧凑、易于阅读的 lambda 表达式,用于已有名称的方法”(请参阅​​ The Java Tutorials - Method References)。

        所以实际上,您是在询问 lambda 表达式的类型。这在JLS §15.27.3 (Type of a Lambda Expression) 中有明确解释。

        简而言之,这里提到了三个兼容性:

        1. 分配上下文
        2. 调用上下文
        3. 投射上下文

        编译器推断出 lambda 表达式或方法引用的类型。由于现在可以(并且必须)考虑多种上下文,Java 8 对类型推断进行了重大改进。

        lambda 表达式的唯一限制是推断的类型必须是functional interface。事实上,相等的 lambda 表达式可以根据上下文有不同的类型。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-12-26
          • 2014-11-25
          • 2016-11-12
          • 1970-01-01
          • 2014-05-08
          • 1970-01-01
          • 2017-05-16
          • 1970-01-01
          相关资源
          最近更新 更多