Java 8中的方法引用是否具有具体类型,如果是,它是什么?

时间:2015-04-26 07:54:56

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

这个问题与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;
  }
}

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

4 个答案:

答案 0 :(得分:6)

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

所以实际上,你要求的是lambda表达式的类型。这在JLS §15.27.3 (Type of a Lambda Expression)明确解释。

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

  1. 作业背景
  2. 调用上下文
  3. 投射上下文
  4. lambda表达式或方法引用的类型由编译器推断。由于现在可以(并且必须)考虑几种上下文,因此Java 8对类型推断进行了大量增强。

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

答案 1 :(得分:6)

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

  

方法引用表达式在赋值上下文中兼容,   如果T是a,则调用上下文或者使用目标类型T转换上下文   功能接口类型(§9.8)和表达式是一致的   从T。

派生的地面目标类型的函数类型      

...

     

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

基本上,方法引用的类型是上下文所期望的类型。独立于上下文,方法引用实际上没有类型。没有办法传递“原始”方法引用,然后将其转换为函数或消费者或稍后的任何内容。

答案 2 :(得分:5)

方法引用只是使用传递参数作为输入参数的函数的语法糖。所以,你可以用这种方式分配它们:

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 :(得分:1)

如果您只有方法参考helloGreeter::makeGreetingFor,则它没有类型。

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

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