我正在学习Java 8中的方法参考资料,但我很难理解为什么会这样做?
class Holder {
private String holded;
public Holder(String holded) {
this.holded = holded;
}
public String getHolded() {
return holded;
}
}
private void run() {
Function<Holder, String> getHolded = Holder::getHolded;
consume(Holder::getHolded); //This is correct...
consume(getHolded); //...but this is not
}
private void consume(Consumer<Holder> consumer) {
consumer.accept(null);
}
正如您在run
方法中看到的那样 - Holder::getHolded
返回未绑定的方法引用,您可以通过将类型为Holder
的对象作为参数来调用它。像这样:getHolded.apply(holder)
但是当它直接作为方法参数调用时,为什么它将这个未绑定的方法引用强制转换为Consumer
,而当我明确地传递Function
时它不会这样做?
答案 0 :(得分:5)
这里有两件事,lambda表达式是poly表达式 - 编译器使用它们的上下文(例如泛型)来推断。
当您声明consume(Holder::getHolded);
时,编译器(在所谓的特殊无效兼容性规则下)会将其推断为Consumer<Holder>
。
这可能看起来不太明显,但想想一个简化的例子。调用方法并丢弃它的返回类型通常不仅仅是好的,对吧?例如:
List<Integer> list = new ArrayList<>();
list.add(1);
即使list.add(1)
返回布尔值,我们也不会关心它。
因此,您的示例可以简化为:
consume(x -> {
x.getHolded(); // ignore the result here
return;
});
所以这些都是可能的和有效的声明:
Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;
但在这种情况下,我们明确地告诉什么类型是Holder::getHolded
,它不是编译器推断,因此consume(getHolded);
失败,{{1毕竟,我是!= Consumer
。
答案 1 :(得分:3)
Java 8在包java.util.function
中引入了4个重要的“函数形状”。
阅读Java docs了解更多详情。
要回答您关于为什么第一个有效但第二个出错的问题,请阅读以下内容:
第二个声明
consume(getHolded);
不起作用,因为参数getHolded
的类型是Function<Holder, String>
,而consume
方法需要类型为Consumer<Holder>
的参数。由于Function
和Consumer
之间没有父子关系,因此需要显式强制转换,否则编译器会错误地输出错误。
第一个声明
consume(Holder::getHolded);
有效,因为方法getHolded
被声明为public String getHolded()
,这意味着它不接受任何参数并返回String
。根据新的void compatibility规则,void类型被推断为包含引用方法的类。请考虑以下声明:
Consumer<Holder> consumer = Holder::getHolded;
即使方法getHolded
不接受任何参数,这也是一个有效的语句。这允许有助于推断空隙类型。另一个例子是你自己提到的那个:
Function<Holder, String> getHolded = Holder::getHolded;
这也是一个有效的陈述,你说过函数对象getHolded
是Function
,它返回String
并接受类型Holder
,即使指定的方法引用不作任何论据。