为什么这个Java 8方法参考编译?

pkl*_*nst 14 java java-8 method-reference

我正在深入探讨 Java 8 Lambda和方法参考等功能.玩了一下让我看到以下示例:

public class ConsumerTest {

  private static final String[] NAMES = {"Tony", "Bruce", "Steve", "Thor"};

   public static void main(String[] args) {
      Arrays.asList(NAMES).forEach(Objects::requireNonNull);
   }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:

为什么main方法中的行会编译?

如果我理解正确的话,引用的方法的签名必须对应于功能接口的SAM签名.在这种情况下,消费者需要以下签名:

void accept(T t);
Run Code Online (Sandbox Code Playgroud)

但是,该requireNonNull方法返回T而不是void:

public static <T> T requireNonNull(T obj)
Run Code Online (Sandbox Code Playgroud)

Mar*_*eel 13

Java语言规范版本8在15.13.2中说:

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

[..]

如果满足以下两个条件,则方法引用表达式与函数类型一致:

  • 函数类型标识与引用相对应的单个编译时声明.
  • 以下之一是真的:
    • 函数类型的结果为void.
    • 函数类型的结果是R,并且将捕获转换(第5.1.10节)应用于所选编译时声明的调用类型(第15.12.2.6节)的返回类型的结果是R'(其中R是可以用于推断R')的目标类型,并且R和R'都不是空的,并且R'在赋值上下文中与R兼容.

(强调我的)

所以函数类型的结果是无效的,这足以让它匹配.

JLS 15.12.2.5还特别提到匹配方法时使用void.对于lambda表达式,存在一个与void兼容的块(15.27.2)的概念,该块在15.12.2.1中引用,但是没有方法引用的等效定义.

我还没有找到更具体的解释(但JLS是一个难以破解的难题,所以也许我错过了一些相关部分),但我认为这与你也被允许调用非事实有关. -void方法作为一个单独的语句(没有赋值return等)).

JLS 15.13.3方法参考的运行时评估还说:

为了确定编译时结果,如果调用方法的结果为void,则方法调用表达式是表达式语句;如果调用方法的结果为非void,则返回语句的表达式.

当方法引用的编译时声明是签名多态时,此确定的效果是:

  • 方法调用的参数类型是相应参数的类型.
  • 方法调用是void或具有Object的返回类型,具体取决于包含方法调用的调用方法是void还是具有返回类型.

因此,生成的方法调用将为void以匹配函数类型.