Search code examples
lambdajava-8functional-interface

Java 8 Lambda translation with incompatiable arguments


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?


Solution

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