为什么我不能将方法引用直接分配给 Object 类型的变量?

Why can I not assign a method reference directly to a variable of Object type?

关于 java-8 语法的简单问题。为什么 JLS-8 限制这样的表达式:

Object of_ref = Stream::of;  // compile-time error

并且只允许像这样的东西:

java.util.function.Function of_ref = Stream::of;
Object obj = of_ref; // compiles ok

?

Object 不是功能接口,方法引用只能分配给功能接口。例如参见 [​​=12=]

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.

那是因为方法引用或 lambda 表达式的目标类型应该是函数式接口。仅基于此,运行时将创建一个 class 的实例,提供给定功能接口的实现。将 lambda 或方法引用视为 abstract 概念。将它分配给功能接口类型赋予它具体的含义。

此外,特定的 lambda 或方法引用可以将多个功能接口作为其目标类型。例如,考虑以下 lamda:

int x = 5;
FunctionalInterface func = (x) -> System.out.println(x);

此 lambda 是 xConsumer。除此之外,任何具有具有以下签名的抽象方法的接口:

public abstract void xxx(int value);

可以用作目标类型。那么,如果将 lambda 分配给 Object 类型,您希望运行时实现哪个接口?这就是为什么您必须显式提供功能接口作为目标类型。

现在,一旦您获得了包含实例的功能接口引用,您就可以将其分配给任何超级引用(包括Object

我怀疑这是一个纯粹的学术问题,因为我看不到任何现实生活中的用例。不过,我很确定它与 Stream::of 作为 lambda 表达式有关。你也可以不这样做:

Object of_ref = list -> Stream.of(list);

我推测准确的 return 类型会告诉编译器正在使用哪个 FunctionalInterface。没有这些信息,编译器就不可能正确、明确地解析 Lambda 表达式。

重点是Java里面没有"function types"。 lambda 表达式本身没有 "type" —— 它可以被输入为 任何 函数接口,其唯一方法的签名与 lambda 匹配。因此,lambda 的类型基于其上下文提供的类型。您必须提供功能接口作为其获取类型的上下文。

考虑相同的问题但对于匿名 classes 是有益的。虽然 lambda 和匿名 classes 在实现上存在差异,但从语义上讲,lambda 本质上等同于匿名 classes 的子集,并且 lambda 表达式总是可以转换为等效的匿名 class ] 创作表达.

当你写:

Function<T, Stream<T>> of_ref = Stream::of;

它等效于使用匿名 classes 的类似以下内容:

Function<T, Stream<T>> of_ref = new Function<T, Stream<T>>() {
    Stream<T> apply(T t) {
        return Stream.of(t);
    }
};

现在考虑

Object of_ref = Stream::of;

匿名 classes 的等价物是什么?

Object of_ref = new [**What goes here?**]() {
    [**What method signature goes here?**] {
        return Stream.of(t);
    }
};

你明白为什么它没有意义了——我们不知道用什么类型作为匿名 class 的基础 class。

你可以!你只需要给编译器多一点信息,让它知道方法引用应该实现什么功能接口:

Object obj = (Function<?, ?>) Stream::of;

方法引用和 lambda 表达式通过使用 类型推断 来确定它们创建的匿名 class 应该实现的接口。如果没有 Function 转换,唯一类型 Java 必须使用的是 Object - 这肯定不是功能接口(只有一个 non-static non-default 方法)。将方法引用表达式显式转换为 Function 提供了我们需要的缺失类型信息,然后我们可以将 Object 字段分配给函数,因为 FunctionObject 的子类型.