Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of double colons - difference between static and non-static method references [duplicate]

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.

like image 517
mkdrive2 Avatar asked Jan 24 '16 23:01

mkdrive2


People also ask

What is static and non-static reference?

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.

What is the use of :: in Java?

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.

What is use of :: in Java 8?

:: 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.


1 Answers

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.

like image 71
finnw Avatar answered Oct 25 '22 12:10

finnw