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

And*_*niy 20 java syntax java-8

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

Object of_ref = Stream::of;  // compile-time error
Run Code Online (Sandbox Code Playgroud)

并允许只有这样的东西:

java.util.function.Function of_ref = Stream::of;
Object obj = of_ref; // compiles ok
Run Code Online (Sandbox Code Playgroud)

ass*_*ias 9

Object不是功能接口,方法引用只能分配给功能接口.参见例如JLS#15.13.2

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

  • 因为`Object`,在JLS引用中表示为`T`,不是函数接口类型. (4认同)

Roh*_*ain 8

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

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

int x = 5;
FunctionalInterface func = (x) -> System.out.println(x);
Run Code Online (Sandbox Code Playgroud)

这拉姆达是Consumerx.除此之外,任何具有以下签名的抽象方法的接口:

public abstract void xxx(int value);
Run Code Online (Sandbox Code Playgroud)

可以用作目标类型.那么,如果将lambda指定为Objecttype ,那么您希望运行时实现哪个接口?这就是为什么你要明确地提供一个功能接口作为目标类型.

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

  • 问题是编译器不知道转换提供的lambda表达式或方法引用的功能接口.例如,方法ref`Btring :: isEmpty`与`Function <String,Boolean>`,`Predicate <String>`以及许多其他方法兼容.编译器通过查看赋值上下文来学习所需的函数类型; `Object`在这里没有提供足够的信息.这与你不能说`new List <String>()`的原因不同; 编译器不知道你想要哪个`List`实现. (6认同)
  • @Andremoniy要将`obj`转换回`Function`,你首先要创建一个`object`吧?那个"对象"的类型是什么? (2认同)

new*_*cct 7

关键是Java 中没有"函数类型".lambda表达式本身没有"类型" - 它可以键入任何功能接口,其唯一方法的签名与lambda匹配.因此,lambda的类型基于其上下文提供的类型.您必须提供一个功能界面作为获取类型的上下文.

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

当你写:

Function<T, Stream<T>> of_ref = Stream::of;
Run Code Online (Sandbox Code Playgroud)

它等同于以下使用匿名类的东西:

Function<T, Stream<T>> of_ref = new Function<T, Stream<T>>() {
    Stream<T> apply(T t) {
        return Stream.of(t);
    }
};
Run Code Online (Sandbox Code Playgroud)

现在考虑

Object of_ref = Stream::of;
Run Code Online (Sandbox Code Playgroud)

什么是匿名类的等价物?

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

你明白为什么它没有意义 - 我们不知道使用什么类型作为匿名类的基类.


Jac*_*ack 6

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

Object obj = (Function<?, ?>) Stream::of;
Run Code Online (Sandbox Code Playgroud)

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