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<Integer> zahlen = new LinkedList<Integer>();
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.
Static reference e to non-static variables println(MyClass. data); i.e. referring a variable using static reference implies to referring using the class name. But, to access instance variables it is a must to create an object, these are not available in the memory, before instantiation.
The double colon (::) operator, also known as method reference operator in Java, is used to call a method by referring to it with the help of its class directly. They behave exactly as the lambda expressions.
:: is a new operator included in Java 8 that is used to refer to a method of an existing class. You can refer to static methods and non-static methods of a class. The only prerequisite for referring to a method is that the method exists in a functional interface, which must be compatible with the method reference.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With