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

Why does this Java 8 method reference compile?

我目前正在深入研究 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);
   }
}

我的问题是:

为什么 main 方法里面的那一行可以编译?

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

void accept(T t);

然而,requireNonNull 方法 returns T 而不是 void:

public static <T> T requireNonNull(T obj)

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

A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.

[..]

A method reference expression is congruent with a function type if both of the following are true:

  • The function type identifies a single compile-time declaration corresponding to the reference.
  • One of the following is true:
    • The result of the function type is void.
    • The result of the function type is R, and the result of applying capture conversion (§5.1.10) to the return type of the invocation type (§15.12.2.6) of the chosen compile-time declaration is R' (where R is the target type that may be used to infer R'), and neither R nor R' is void, and R' is compatible with R in an assignment context.

(强调我的)

因此函数类型的结果为 void 这一事实足以让它匹配。

JLS 15.12.2.5 also specifically mentions the use of void when matching methods. For lambda expression there is the notion of a void-compatible block (15.27.2) which is referenced in 15.12.2.1,但没有对方法引用的等效定义。

我没能找到更具体的解释(但 JLS 是一个棘手的问题,所以也许我遗漏了一些相关部分),但我认为这与你是还允许调用非 void 方法作为其自身的语句(无赋值,return 等))。

JLS 15.13.3 Run-Time Evaluation of Method References 还说:

For the purpose of determining the compile-time result, the method invocation expression is an expression statement if the invocation method's result is void, and the Expression of a return statement if the invocation method's result is non-void.

The effect of this determination when the compile-time declaration of the method reference is signature polymorphic is that:

  • The types of the parameters for the method invocation are the types of the corresponding arguments.
  • The method invocation is either void or has a return type of Object, depending on whether the invocation method which encloses the method invocation is void or has a return type.

因此生成的方法调用将无效以匹配功能类型。

除了它在@Mark Rotteveel 的确切答案中所说的,它编译是因为在 Java 中你可以忽略任何方法调用的结果,例如在下面的例子中:

Map<String, String> map = new HashMap<>();

map.put("1", "a");

map.put("1", "A"); // Who cares about the returned value "a"?

由于您没有在 forEach() 消费者块中返回任何内容,因此根据规范它是有效的。