6

I found an interesting example of using Stream API:

Stream<String> stream = Stream.of("w", "o", "l", "f");
BiConsumer<StringBuilder, String> append = StringBuilder::append;
StringBuilder collected = stream.collect(StringBuilder::new, append, StringBuilder::append);
System.out.println(collected); //it works correctly

Stream.collect takes three parameters:

Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner

BiConsumer takes two parameters and doesn't return anything. Why this line compiles and works?

BiConsumer<StringBuilder, String> append = StringBuilder::append;

StringBuilder doesn't have void method append(java.lang.StringBuilder, java.lang.String).

A. Segen
  • 71
  • 2
  • 5
    The first argument becomes the object on which the method is invoked. And `StringBuilder.append(String)` does exist. – Tunaki May 04 '16 at 20:05
  • 2
    One thing that helps me to wrap my head around it is that a member function is really no different than a static function with an extra parameter for `this`. Whenever you see `ClassName::memberFunction`, the first parameter is always `ClassName this` – Hank D May 04 '16 at 20:52
  • I don't see an answer for this in the mentioned duplicate. Any body care to elaborate? I have the same problem. Since append is not a void method and BiConsumer's non abstract method has a void method how above code compiles? – Asiri Liyana Arachchi Feb 10 '17 at 14:24

2 Answers2

3

JLS 15.13.3 specifies more or less that the receiver -- the object the method is being called on -- can become the first argument for the functional interface:

If the form is ReferenceType :: [TypeArguments] Identifier, the body of the invocation method similarly has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

The invocation mode is derived from the compile-time declaration as specified in §15.12.3.

If the compile-time declaration is an instance method, then the target reference is the first formal parameter of the invocation method. Otherwise, there is no target reference.

If the compile-time declaration is an instance method, then the arguments to the method invocation expression (if any) are the second and subsequent formal parameters of the invocation method. Otherwise, the arguments to the method invocation expression are the formal parameters of the invocation method.

The compile-time declaration is in fact an instance method, so the StringBuilder becomes the first parameter of the invocation method, and the String becomes the second.

In other words, the method reference SomeClass::instanceMethod is equivalent to the lambda (SomeClass receiver, args...) -> receiver.instanceMethod(args...).

Community
  • 1
  • 1
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
2

The first type argument of the BiConsumer is the type the method is applied on, and the second is the method's single parameter. The classic example would be ArrayList::add.

Mureinik
  • 297,002
  • 52
  • 306
  • 350