Method Reference - passing Function to method with Consumer argument

人走茶凉 提交于 2019-11-29 07:50:31

Two things here, lambda expressions are poly expressions - they are inferred by the compiler using their context (like generics for example).

When you declare consume(Holder::getHolded);, compiler (under the so-called special void compatibility rule) will infer it to Consumer<Holder>.

And this might not look obvious, but think of a simplified example. It is generally more than ok do call a method and discard it's return type, right? For example:

List<Integer> list = new ArrayList<>();
list.add(1);

Even if list.add(1) returns a boolean, we don't care about it.

Thus your example that works can be simplified to:

consume(x -> {
        x.getHolded(); // ignore the result here
        return;
});

So these are both possible and valid declarations:

Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;

But in this case we are explicitly telling what type is Holder::getHolded,, it's not the compiler inferring, thus consume(getHolded); fails, a Consumer != Function after all.

Java 8 introduced 4 important "function shapes" in the package java.util.function.

  • Consumer -> accepts a method reference (or a lambda expression) that takes one argument but doesn't return anything
  • Supplier -> accepts a method reference (or a lambda expression) that takes no argument and returns an object.
  • Function -> accepts a method reference (or a lambda expression) that takes one argument and returns an object.
  • Predicate -> accepts a method reference (or a lambda expression) that takes one argument and returns a boolean.

Read the Java docs for more detail.

To answer your question on why the first one works but the second one errors out, read following:

The second statement

consume(getHolded);

doesn't work because the type of the argument getHolded is Function<Holder, String> whereas the consume method expects an argument of type Consumer<Holder>. Since there is no parent-child relationship between Function and Consumer, it requires an explicit cast without which the compiler rightly errors out.

The first statement

consume(Holder::getHolded);

works because the method getHolded is declared as public String getHolded() meaning that it doesn't take any argument and returns a String. As per the new void compatibility rule, void types are inferred as the class containing the referenced method. Consider the following statement:

Consumer<Holder> consumer = Holder::getHolded;

This is a valid statement even though the method getHolded doesn't accept any arguments. This is allowed to facilitate inferring void types. Yet another example is the one you have mentioned yourself:

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

This is also a valid statement where you have said that the function object getHolded is a Function that returns String and accepts a type Holder even though the assigned method reference doesn't take any argument.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!