Java 8: Difference between method reference Bound Receiver and UnBound Receiver

The idea of the unBound receiver such as String::length is you're referring to a method of an object that will be supplied as one of the lambda's parameters. For example, the lambda expression (String s) -> s.toUpperCase() can be rewritten as String::toUpperCase.

But Bounded refers to a situation when you’re calling a method in a lambda to an external object that already exists. For example, the lambda expression () -> expensiveTransaction.getValue() can be rewritten as expensiveTransaction::getValue.

Situations for three different ways of method reference

(args) -> ClassName.staticMethod(args) can be ClassName::staticMethod // This is static (you can think as unBound also)

(arg0, rest) -> arg0.instanceMethod(rest) can be ClassName::instanceMethod (arg0 is of type ClassName) // This is unBound

(args) -> expr.instanceMethod(args) can be expr::instanceMethod // This is Bound

Answer retrieved from Java 8 in Action book


When you want the method to be executed for a specific instance of some class, you use a bound receiver.

For example :

Stream.of("x","y").forEach(System.out::println);

will execute println on a sepcific instance of PrintStream - the System.out instance. Therefore System.out.println("x") and System.out.println("y") will be executed as a result of passing that method reference to forEach.

On the other hand, if you want the method to be executed for an unspecified instance of a class, you can use a unbound receiver.

For example :

Stream.of("x","y","").filter(String::isEmpty);

will execute isEmpty() on each of the String instances of the Stream - i.e. "x".isEmpty(), "y".isEmpty() and "".isEmpty().


Basically, unbound receivers allow you to use instance methods as if they were static methods with a first parameter of the declaring type - so you can use them as functions by passing in whatever instance you want. With a bound receiver, the "target" instance is effectively part of the function.

An example might make this clearer:

import java.util.function.*;

public class Test {

    private final String name;

    public Test(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Test t1 = new Test("t1");
        Test t2 = new Test("t2");

        Supplier<String> supplier = t2::method;
        Function<Test, String> function = Test::method;

        // No need to say which instance to call it on -
        // the supplier is bound to t2            
        System.out.println(supplier.get());

        // The function is unbound, so you need to specify
        // which instance to call it on
        System.out.println(function.apply(t1));
        System.out.println(function.apply(t2));
    }

    public String method() {
        return name;
    }
}