2
import org.junit.Test;

import java.util.stream.IntStream;

public class GomanTest {

    @Test
    public void someTest() {
        IntStream.of(2, 3, 1).collect(Container::new, Container::add, null);
    }
}


class Container<T> {

    void add(T t) {
        System.out.println("this is container " + t);
    }
}

output:

this is container 2
this is container 3
this is container 1

This runs successfully on 1.8.0_45.jdk. How does Container#add get translated to ObjIntConsumer#accept?

mert inan
  • 1,537
  • 2
  • 15
  • 26

1 Answers1

6

The method Container.add is an instance method which requires an instance to be invoked on. Since a method reference on the form ClassName::methodName is not bound to an instance, Container::add has a functional signature of (Container<T>,T).

As you didn’t specify type arguments for Container nor a target type, the compiler will infer Container<Object>. So Container::add has the inferred signature (Container<Object>,Object) here, which is appropriate for the accept method of ObjIntConsumer<Container<Object>>, which has the signature (Container<Object>,int).

The second argument can accept a value of type int, as, after boxing it to Integer, it is assignable to Object.

The same works if you assign the result to a variable, thus providing a target type, of Container<Object> or Container<Integer> like

Container<Integer> collected
   = IntStream.of(2, 3, 1).collect(Container::new, Container::add, null);

Any type argument which can consume an Integer, e.g. Serializable or Number, will work as well.


You can read more about unbounded references to instance methods in “What does ‘an Arbitrary Object of a Particular Type’ mean in java 8?”.


As a side note, the collect methods of streams are not supposed to accept null arguments and the Stream implementation doesn’t. That passing null works with the primitive streams is a glitch in the current implementation and code which passes null is likely to break in the next version. As Tagir Valeev pointed out, the behavior has already changed in the current development state of Java 9.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • 4
    By the way, I’m surprised that `IntStream` accepts `null` as last argument to `collect`. The standard `Stream` implementation does not. – Holger Sep 15 '15 at 11:03
  • 3
    It's [already fixed](http://hg.openjdk.java.net/jdk9/dev/jdk/diff/013baa71b58b/src/share/classes/java/util/stream/IntPipeline.java) in JDK-9, thus I would not write it this way – Tagir Valeev Sep 15 '15 at 11:26
  • My confusion is not around boxing, unboxing. I provide method with one argument "add(T t)" but that gets translated to 2 arguments accept(T t, int value). I expect something like this: required: java.lang.Integer found: java.lang.Integer,int reason: actual and formal argument lists differ in length – mert inan Sep 15 '15 at 13:35
  • 1
    The `Container.add` method is *not* `static`. Therefore it requires a `Container` instance to invoke it, *plus* an instance of `T` to pass as argument. Makes a *functional* signature of `(Container,T)`. See [“What does ‘an Arbitrary Object of a Particular Type’ mean in java 8?”](http://stackoverflow.com/q/23533345/2711488). – Holger Sep 15 '15 at 14:04