方法参考 - 使用Consumer参数将Function传递给方法

时间:2018-05-16 18:28:04

标签: java-8 method-reference functional-interface

我正在学习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时它不会这样做?

2 个答案:

答案 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个重要的“函数形状”。

  • 消费者 - &gt;接受一个方法引用(或一个lambda表达式),它接受一个参数但不返回任何内容
  • 供应商 - &gt;接受不带参数的方法引用(或lambda表达式)并返回一个对象。
  • 功能 - &gt;接受一个方法引用(或一个lambda表达式),它接受一个参数并返回一个对象。
  • 谓词 - &gt;接受一个方法引用(或一个lambda表达式),它接受一个参数并返回一个布尔值。

阅读Java docs了解更多详情。

要回答您关于为什么第一个有效但第二个出错的问题,请阅读以下内容:

第二个声明

consume(getHolded);

不起作用,因为参数getHolded的类型是Function<Holder, String>,而consume方法需要类型为Consumer<Holder>的参数。由于FunctionConsumer之间没有父子关系,因此需要显式强制转换,否则编译器会错误地输出错误。

第一个声明

consume(Holder::getHolded);

有效,因为方法getHolded被声明为public String getHolded(),这意味着它不接受任何参数并返回String。根据新的void compatibility规则,void类型被推断为包含引用方法的类。请考虑以下声明:

Consumer<Holder> consumer = Holder::getHolded;

即使方法getHolded不接受任何参数,这也是一个有效的语句。这允许有助于推断空隙类型。另一个例子是你自己提到的那个:

Function<Holder, String> getHolded = Holder::getHolded;

这也是一个有效的陈述,你说过函数对象getHoldedFunction,它返回String并接受类型Holder,即使指定的方法引用不作任何论据。