关于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
答案 0 :(得分:9)
Object
不是功能接口,方法引用只能分配给功能接口。请参阅示例JLS #15.13.2
如果T是函数接口类型(第9.8节)且表达式与地面目标类型的函数类型一致,则方法引用表达式在赋值上下文,调用上下文或具有目标类型T的转换上下文中是兼容的源自T。
答案 1 :(得分:8)
那是因为方法引用的目标类型或lambda表达式应该是一个功能接口。仅基于此,运行时将创建提供给定功能接口的实现的类的实例。将lambdas或方法引用视为abstract
概念。将其分配给功能接口类型赋予其具体含义。
此外,特定的lambda或方法引用可以具有多个功能接口作为其目标类型。例如,请考虑以下lamda:
int x = 5;
FunctionalInterface func = (x) -> System.out.println(x);
此lambda是Consumer
的{{1}}。除此之外,任何具有以下签名的抽象方法的接口:
x
可用作目标类型。那么,如果将lambda分配给public abstract void xxx(int value);
类型,那么您希望运行时实现哪个接口?这就是为什么你要明确地提供一个功能接口作为目标类型。
现在,一旦您获得了一个包含实例的功能界面引用,您就可以将其分配给任何超级引用(包括Object
)
答案 2 :(得分:7)
关键是 Java中没有"函数类型" 。 lambda表达式没有"类型"它本身 - 可以输入任何功能接口,其唯一方法的签名与lambda匹配。因此,lambda类型基于其上下文提供的类型。您必须提供一个功能界面作为获取类型的上下文。
考虑同样的问题但对于匿名类是有益的。尽管lambdas和匿名类之间存在实现差异,但从语义上讲,lambdas本质上等同于匿名类的子集,并且lambda表达式总是可以转换为等效的匿名类创建表达式。
当你写:
Function<T, Stream<T>> of_ref = Stream::of;
它等同于使用匿名类的以下内容:
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;
与匿名类相同的是什么?
Object of_ref = new [**What goes here?**]() {
[**What method signature goes here?**] {
return Stream.of(t);
}
};
你明白为什么它没有意义 - 我们不知道使用哪种类型作为匿名类的基类。
答案 3 :(得分:3)
我怀疑这是一个纯粹的学术问题,因为我无法看到任何真实的用例。不过,我很确定它与Stream::of
是一个lambda表达式有关。你也可能不这样做:
Object of_ref = list -> Stream.of(list);
我推测一个准确的返回类型告诉编译器它正在使用哪个FunctionalInterface。如果没有这些信息,编译器就无法正确且明确地解析Lambda表达式。
答案 4 :(得分:2)
你可以!你只需要给编译器一些信息,这样它就知道方法引用应该实现什么功能接口:
Object obj = (Function<?, ?>) Stream::of;
方法引用和lambda表达式通过使用类型推断来确定它们创建的匿名类应该实现的接口。如果没有Function
强制转换,Java必须使用的唯一类型是Object
- 这当然不是一个功能接口(只有一个非静态非默认方法的接口)。将方法引用表达式显式地转换为Function
提供了我们需要的缺失类型信息,然后我们可以将Object
字段分配给函数,因为Function
是Object
的子类型