4

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

那么,Java 8 中方法引用的类型是什么?下面是如何将方法引用“转换”(提升?)到 a 中的一个小演示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;
  }
}

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

4

4 回答 4

6

Method references are just a syntactic sugar for a function that uses passed parameter as an input argument. So, you can assign them this way:

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

types are inferred and depend on a context.

Your case:

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

and it is equal to:

Function<String, String> foo = s -> helloGreeter.makeGreetingFor(s);
于 2015-04-26T08:10:05.380 回答
6

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

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

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

  1. 赋值上下文
  2. 调用上下文
  3. 铸造上下文

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

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

于 2015-04-26T08:16:37.047 回答
6

来自JLS,第 15.13.2 节,“方法引用的类型”

如果 T 是函数接口类型(第 9.8 节)并且表达式与从 T 派生的基本目标类型的函数类型一致,则方法引用表达式在赋值上下文、调用上下文或转换上下文中与目标类型 T 兼容.

...

如果方法引用表达式与目标类型 T 兼容,则表达式的类型 U 是从 T 派生的基本目标类型。

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

于 2015-04-26T08:16:44.123 回答
1

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

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

String greeting =
    ((Function<String, String>)helloGreeter::makeGreetingFor)
        .apply("Joe");
于 2015-04-26T09:07:49.723 回答