Harmeet Singh Taara
Harmeet Singh Taara

Reputation: 6611

Difference between method reference Bound Receiver and Unbound Receiver

I am trying to use Java 8 method references in my code. There are four types of method references available.

  1. Static method reference.
  2. Instance method (bound receiver).
  3. Instance method (unbound receiver).
  4. Constructor reference.

With static method reference and constructor reference I have no problem, but instance method (bound receiver) and instance method (unbound receiver) really confused me. In bound receiver, we are using an object reference variable for calling a method like:

objectRef::Instance Method

In unbound receiver we are using class name for calling a method like:

ClassName::Instance Method.

I have the following question:

  1. What is the need for different types of method references for instance methods?
  2. What is the difference between bound and unbound receiver method references?
  3. Where should we use bound receiver and where should we use unbound receiver?

I also found the explanation of bound and unbound receiver from Java 8 language features books, but was still confused with the actual concept.

Upvotes: 21

Views: 10175

Answers (7)

Johnny Willer
Johnny Willer

Reputation: 3917

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

Upvotes: 31

jumping_monkey
jumping_monkey

Reputation: 7819

A very good article i found here:

References to bound non-static methods

A bound non-static method reference refers to a non-static method that's bound to a receiver object. Its syntax is objectName::instanceMethodName, where objectName identifies the receiver and instanceMethodName identifies the instance method. An example is s::trim. Listing 2 demonstrates a bound non-static method reference.

Listing 2. MRDemo version 2: A bound non-static method reference

import java.util.function.Supplier;

public class MRDemo
{
   public static void main(String[] args)
   {
      String s = "The quick brown fox jumped over the lazy dog";
      print(s::length);
      print(() -> s.length());
      print(new Supplier<Integer>()
      {
         @Override
         public Integer get()
         {
            return s.length(); // closes over s
         }
      });
   }

   public static void print(Supplier<Integer> supplier)
   {
      System.out.println(supplier.get());
   }
}

Listing 2's main() method assigns a string to String variable s and then invokes the print() class method with functionality to obtain the string's length as this method's argument. print() is invoked in method reference (s::length -- length() is bound to s), equivalent lambda, and equivalent anonymous class contexts.

I've defined print() to use the java.util.function.Supplier predefined functional interface, whose get() method returns a supplier of results. In this case, the Supplier instance passed to print() implements its get() method to return s.length(); print() outputs this length.

s::length introduces a closure that closes over s. You can see this more clearly in the lambda example. Because the lambda has no arguments, the value of s is only available from the enclosing scope. Therefore, the lambda body is a closure that closes over s. The anonymous class example makes this even clearer.

Compile Listing 2 and run the application. You'll observe the following output:

44
44
44

References to unbound non-static methods

An unbound non-static method reference refers to a non-static method that's not bound to a receiver object. Its syntax is className::instanceMethodName, where className identifies the class that declares the instance method and instanceMethodName identifies the instance method. An example is String::toLowerCase.

String::toLowerCase is an unbound non-static method reference that identifies the non-static String toLowerCase() method of the String class. However, because a non-static method still requires a receiver object (in this example a String object, which is used to invoke toLowerCase() via the method reference), the receiver object is created by the virtual machine. toLowerCase() will be invoked on this object. String::toLowerCase specifies a method that takes a single String argument, which is the receiver object, and returns a String result. String::toLowerCase() is equivalent to lambda (String s) -> { return s.toLowerCase(); }.

Listing 3 demonstrates this unbound non-static method reference.

Listing 3. MRDemo version 3: Unbound non-static method reference

import java.util.function.Function;

public class MRDemo
{
   public static void main(String[] args)
   {
      print(String::toLowerCase, "STRING TO LOWERCASE");
      print(s -> s.toLowerCase(), "STRING TO LOWERCASE");
      print(new Function<String, String>()
      {
         @Override
         public String apply(String s) // receives argument in parameter s;
         {                             // doesn't need to close over s
            return s.toLowerCase();
         }
      }, "STRING TO LOWERCASE");
   }

   public static void print(Function<String, String> function, String
s)
   {
      System.out.println(function.apply(s));
   }
}

Listing 3's main() method invokes the print() class method with functionality to convert a string to lowercase along with the string to be converted as the method's arguments. print() is invoked in method reference (String::toLowerCase -- toLowerCase() isn't bound to a user-specified object), equivalent lambda, and equivalent anonymous class contexts.

I've defined print() to use the java.util.function.Function predefined functional interface, which represents a function that accepts one argument and produces a result. In this case, the Function instance passed to print() implements its R apply(T t) method to return s.toLowerCase(); print() outputs this string.

Although the String part of String::toLowerCase makes it look like a class is being referenced, only an instance of this class is referenced. The anonymous class example makes this fact more obvious. Note that the anonymous class example shows that the lambda receives an argument; it doesn't close over parameter s and is therefore not a closure.

Compile Listing 3 and run the application. You'll observe the following output:

string to lowercase
string to lowercase
string to lowercase

Upvotes: 0

Bharath
Bharath

Reputation: 1807

Along with the excellent answers from above. Thanks to the wonderful explanation by joshua bloch, effective java third edition. I was finally able to wrap my head around what bounded and unbounded reference means.

In bounded reference, the receiving object is specified in the method reference. Bound references are similar in nature to static references: the function object takes the same arguments as the referenced method.

In unbound references, the receiving object is specified when the function object is applied, via an additional parameter before the method’s declared parameters. Unbound references are often used as mapping and filter functions in stream pipelines

Finally, there are two kinds of constructor references, for classes and arrays. Constructor references serve as factory objects.

Type of Method Ref |    Example              |    Lambda Equivalent
───────────────────┼─────────────────────────┼───────────────────────────────
Static             | Integer::parseInt       |  str -> Integer.parseInt(str)
Bound              | Instant.now()::isAfter  |  Instant then = Instant.now(); 
                   |                         |  t -> then.isAfter(t)
Unbound            | String::toLowerCase     |  str -> str.toLowerCase()
Class Constructor  | TreeMap<K,V>::new       |  () -> new TreeMap
Array Constructor  | int[]::new              |  len -> new int[len]

Upvotes: 0

Ray Eldath
Ray Eldath

Reputation: 423

Here's an example:

public static void main(String[] args) {
    // unbound
    UnaryOperator<String> u = String::toUpperCase;
    System.out.println(u.apply("hello"));

    // bound
    String a = "hello";
    Supplier<String> r = a::toUpperCase;
    System.out.println(r.get());
}

which will output two lines of HELLO.

Upvotes: 1

ZiglioUK
ZiglioUK

Reputation: 2610

I've captured this from a recent presentation

enter image description here

Upvotes: 3

Jon Skeet
Jon Skeet

Reputation: 1500893

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;
    }
}

Upvotes: 13

Eran
Eran

Reputation: 393846

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().

Upvotes: 4

Related Questions