Java 为什么我不能将方法引用直接分配给对象类型的变量?
关于Java 为什么我不能将方法引用直接分配给对象类型的变量?,java,syntax,java-8,Java,Syntax,Java 8,关于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 ?对象不是功能接口,方法引用只能分配给功能接口。例如,见 如果T是函数接口类型(§9.8),且表达式与从T派生的基本目标类型的函数类型
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
?
对象
不是功能接口,方法引用只能分配给功能接口。例如,见
如果T是函数接口类型(§9.8),且表达式与从T派生的基本目标类型的函数类型一致,则方法引用表达式在赋值上下文、调用上下文或转换上下文中与目标类型T兼容
这是因为方法引用或lambda表达式的目标类型应该是函数接口。仅基于此,运行时将创建一个类的实例,提供给定函数接口的实现。将lambdas或方法引用视为
抽象概念。将其指定给函数接口类型赋予了它具体的含义
此外,一个特定的lambda或方法引用可以有多个函数接口作为其目标类型。例如,考虑下面的LAMDA:
int x = 5;
FunctionalInterface func = (x) -> System.out.println(x);
此lambda是x
的消费者
。除此之外,具有以下签名的单个抽象方法的任何接口:
public abstract void xxx(int value);
可以用作目标类型。那么,如果将lambda分配给对象
类型,您希望运行时实现哪个接口?这就是为什么您必须显式地提供一个函数接口作为目标类型
现在,一旦您获得了一个包含实例的函数接口引用,您就可以将其分配给任何超级引用(包括对象)我怀疑这是一个纯粹的学术问题,因为我看不到这方面的任何实际用例。尽管如此,我很确定这与Stream::of
是lambda表达式有关。您也不能这样做:
Object of_ref = list -> Stream.of(list);
我推测一个精确的返回类型会告诉编译器它正在使用哪个。没有这些信息,编译器就不可能正确、明确地解析Lambda表达式。关键是Java中没有“函数类型”。lambda表达式本身没有“类型”——它可以被类型化到任何函数接口,只要该函数接口的唯一方法签名与lambda匹配。因此,lambda的类型基于其上下文提供的类型。您必须提供一个函数接口作为获取类型的上下文
考虑相同的问题,但对于匿名类是有启发性的。尽管lambda和匿名类之间存在实现差异,但在语义上,lambda本质上等同于匿名类的子集,并且lambda表达式始终可以转换为等效的匿名类创建表达式
当你写作时:
Function<T, Stream<T>> of_ref = Stream::of;
匿名类的等价物是什么
Object of_ref = new [**What goes here?**]() {
[**What method signature goes here?**] {
return Stream.of(t);
}
};
您知道为什么它没有意义了——我们不知道使用什么类型作为匿名类的基类。您可以您只需要向编译器提供多一点信息,让它知道方法引用应该实现什么样的函数接口:
Object obj = (Function<?, ?>) Stream::of;
objectobj=(函数)Stream::of;
方法引用和lambda表达式通过使用类型推断来确定它们创建的匿名类应该实现什么接口。如果没有函数
强制转换,Java必须处理的唯一类型就是对象
——它肯定不是功能接口(只有一个非静态非默认方法的接口)。显式地将方法引用表达式强制转换为函数
提供了所需的缺失类型信息,然后我们可以将对象
字段分配给函数,因为函数
是对象
的子类型,在您的第一个不可编译代码段中,您希望在\u ref的中存储什么?引用的实例的具体类型是什么?谢谢,但我问为什么我不能直接将此引用分配给对象
?因为对象
,在JLS引号中表示为T
,不是功能接口类型。谢谢,但对我来说这听起来很逻辑:我可以将我的obj
转换回函数
并调用apply()
,所以在检索方法引用时我不能直接这样做?@Andremoniy要将obj
转换回函数
,你必须首先创建一个对象
,对吗?该对象的类型是什么?啊,好的,我理解您的逻辑。问题是编译器不知道将提供的lambda表达式或方法引用转换到哪个函数接口。例如,方法refString::isEmpty
与函数
、谓词
以及许多其他方法兼容。编译器通过查看赋值上下文来学习所需的函数类型<代码>对象
此处未提供足够的信息。这与为什么不能说newlist()
没有什么不同;编译器不知道您想要的是List
的哪个实现。请注意,添加强制转换足以让编译器推断出要实现的正确函数接口:Object obj=(Function)Stream::of
Ok!这意味着它只是句法上的糖,是吗?@Andremoniy:嗯,这取决于你如何定义“句法糖”。在某些情况下,需要进行一些转换(例如,lambda中的this
相当于OuterClass。在匿名类中,this
,而不是this
),并且存在一些行为差异,例如两个匿名类对象是不同的(!=
),但两个lambda可能不是。但在大多数情况下,你可以把它看作是一种句法上的糖分。
Object of_ref = new [**What goes here?**]() {
[**What method signature goes here?**] {
return Stream.of(t);
}
};
Object obj = (Function<?, ?>) Stream::of;