Java 8 builds and reduces

As you know, when performing accumulation, “reduction” always returns a new immutable object, while “collect” will make changes to the mutable object.

However, when I accidentally assign one link to a reduction and collection method, it compiles without any errors. Why?

Take a look at the following code:

public class Test { @Test public void testReduce() { BiFunction<MutableContainer,Long,MutableContainer> func = MutableContainer::reduce; // Why this can compile? BiConsumer<MutableContainer,Long> consume = MutableContainer::reduce; // correct way: //BiConsumer<MutableContainer,Long> consume = // MutableContainer::collect; long param=10; MutableContainer container = new MutableContainer(0); consume.accept(container, param); // here prints "0",incorrect result, // because here we expect a mutable change instead of returning a immutable value System.out.println(container.getSum()); MutableContainer newContainer = func.apply(container, param); System.out.println(newContainer.getSum()); } } class MutableContainer { public MutableContainer(long sum) { this.sum = sum; } public long getSum() { return sum; } public void setSum(long sum) { this.sum = sum; } private long sum; public MutableContainer reduce(long param) { return new MutableContainer(param); } public void collect(long param){ this.setSum(param); } } 
+5
source share
1 answer

Basically, the question boils down to the following: BiConsumer is a functional interface whose function is declared as follows:

 void accept(T t, U u) 

You gave a link to a method with the correct parameters, but the return type is incorrect:

 public MutableContainer reduce(long param) { return new MutableContainer(param); } 

[The T parameter is actually the this object when reduce is called, since reduce is an instance method, not a static method. Therefore, the parameters are correct.] The return type is MutableContainer , not void . So the question is, why does the compiler accept it?

Intuitively, I think this is because the method reference is more or less equivalent to an anonymous class, which looks like this:

 new BiConsumer<MutableContainer,Long>() { @Override public void accept(MutableContainer t, Long u) { t.reduce(u); } } 

Note that t.reduce(u) will return the result. However, the result is discarded. Since it is normal to call a method with a result and cancel the result, I think that, by and large, why it should use the method reference, where the method returns the result, for a functional interface whose method returns void .

Legally, I believe that the reason is in JLS 12.15.2.5 . This section is difficult, and I do not quite understand it, but somewhere in this section it says:

If e is the exact reference expression of the method ... R 2 is invalid.

where, if I read it correctly, R 2 is the result of a type of functional interface method. I think this is a suggestion that allows you to use a reference to a non-military method when a reference to the void method is expected.

( Edit: As Ismail noted in the comments, JLS 15.13.2 may be the right article here, he says that the reference to the method matches the type of the function, and one of the conditions for this is that the result of the type of the function is void .)

In any case, this should hopefully explain why it compiles. Of course, the compiler cannot always say when you are doing something that will lead to incorrect results.

+5
source

Source: https://habr.com/ru/post/1209978/


All Articles