Thursday, July 4, 2019

java - Use of double colons - difference between static and non-static method references




Edit: My question here was answered. To summarize, I was confused about the usage of non-static method references. There the functional interface and referenced method have a different number of parameters.



What answered my question is the comment and the accepted answer.






I am currently reading the Java Tutorial about Stream reduction methods (https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html). There I found a piece of code that I thought was wrong, so I made a simpler code to make sure.




// B.java file
import java.util.*;

public class B
{
public static void main(String[] args)
{
List zahlen = new LinkedList();
zahlen.add(1);
zahlen.add(2);

zahlen.add(3);
Averager averageCollect = zahlen.stream()
.collect(Averager::new, Averager::addcount, Averager::combine);
System.out.println(averageCollect.average());
}
}

// Averager.java from the official Java tutorial
public class Averager
{

private int total = 0;
private int count = 0;

public double average() {
return count > 0 ? ((double) total)/count : 0;
}

public void addcount(int i) { total += i; count++;}
public void combine(Averager other) {
total += other.total;

count += other.count;
}
}


The reason I thought this wouldn't work is because of the line:



Averager averageCollect = zahlen.stream()
.collect(Averager::new, Averager::addcount, Averager::combine);



In the Java documentation for the Stream.collect (https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.function.Supplier-java.util.function.BiConsumer-java.util.function.BiConsumer-) it says that as the second parameter a function which matches the functional interface BiConsumer is required which has an abstract method with two arguments. But Averager.addcount and Averager.combine only have one parameter.



I also checked with lambda expressions:



Averager averageCollect = zahlen.stream()
.collect(Averager::new, (a,b) -> a.addcount(b), (a,b) -> a.combine(b));


This code also works and as the second and third parameter I have functions with two parameters.




Why exactly does the code I wrote above work, even though functions with only one parameter were given? And why are there error messages when I change both Averager.addcount and Averager.combine to have two parameters like this?



public void addcount(Averager one, Integer i)
public void combine(Averager one, Averager other)


If I do that I get the following error message:



B.java:12: error: no suitable method found for collect(Averager::new,Averager::addcount,Averager::combine)

.collect(Averager::new, Averager::addcount, Averager::combine);
^
method Stream.collect(Supplier,BiConsumer,BiConsumer) is not applicable
(cannot infer type-variable(s) R#1
(argument mismatch; invalid method reference
cannot find symbol
symbol: method addcount(R#1,Integer)
location: class Averager))
method Stream.collect(Collector) is not applicable
(cannot infer type-variable(s) R#2,A

(actual and formal argument lists differ in length))
where R#1,T,R#2,A are type-variables:
R#1 extends Object declared in method collect(Supplier,BiConsumer,BiConsumer)
T extends Object declared in interface Stream
R#2 extends Object declared in method collect(Collector)
A extends Object declared in method collect(Collector)
1 error


Please help me understand.


Answer




Averager averageCollect = zahlen.stream()
.collect(Averager::new, Averager::addcount, Averager::combine);


This is fine. It is equivalent to



Averager averageCollect = zahlen.stream()
.collect(() -> new Averager(),
(myAverager, n) -> myAverager.addcount(n),
(dst, src) -> dst.combine(src))



Remember every nonstatic method has a hidden this parameter. In this case it is (correctly) binding this to the first argument of the accumulator and combiner callbacks.



It will also work with static methods such as:



public static void addcount(Averager a, int i) {
a.total += i;
a.count++;
}

public static void combine(Averager dst, Averager src) {
dst.total += src.total;
dst.count += src.count;
}


which hopefully makes it clearer what is happening.



But there is no need to change the code.


No comments:

Post a Comment

plot explanation - Why did Peaches' mom hang on the tree? - Movies & TV

In the middle of the movie Ice Age: Continental Drift Peaches' mom asked Peaches to go to sleep. Then, she hung on the tree. This parti...