Bounded Instance Method References
When the body of a lambda expression is a call to an instance method, the method reference specified depends on whether the object on which the instance method is invoked exists or not at the time the method reference is defined.
In the code below, the reference sb is declared and initialized at (1). It is effectively final when accessed in the lambda expression at (2a). The reference value of the reference sb is captured by the lambda expression. When the lambda expression is executed at a later time, the reverse() method is executed on the object denoted by the reference sb. In this case, the bounded instance method reference is simply the reference and the instance method name, separated by the double-colon delimiter, as shown at (2b). The bounded instance method reference at (2b) can replace the lambda expression at (2a). It is executed when the functional method get() of the parameterized functional interface Supplier<StringBuilder> is called, as shown at (3).
StringBuilder sb = new StringBuilder(“!em esreveR”); // (1)
// () -> StringBuilder
Supplier<StringBuilder> sbReverserLE = () -> sb.reverse(); // (2a)
Supplier<StringBuilder> sbReverserMR = sb::reverse; // (2b)
System.out.println(sbReverserMR.get()); // (3)
// Calls sb.reverse() that returns the StringBuilder with character sequence
// “Reverse me!”.
The case where the object on which the instance method is executed does not exist when the method reference is defined, but will be supplied when the method reference is executed, requires an unbounded instance method reference (p. 729).
The following rule can be used for converting between a lambda expression and a bounded instance method reference:
A lambda expression of the form
(args) -> expr.instanceMethod(args)
is semantically equal to the bounded instance method reference:
expr::instanceMethod
where expr is an expression that evaluates to a reference value that is captured by the bounded instance method reference and becomes the target reference for the bounded instance method reference.
Any reference involved in the evaluation of expr must be effectively final, and is captured by the bounded instance method reference. The target reference represented by expr is separated from the instance method name by the double-colon delimiter. The target reference is fixed when the bounded instance method reference is defined. The instance method is invoked on the object denoted by the target reference when the method reference is executed at a later time, and any arguments required by the instance method are passed at the same time.
Given an ArrayList<String> denoted by the reference words, we can pass the method reference System::println to the forEach() method of the ArrayList<E> class in order to print each element of the list. The method takes a consumer as an argument, and the type of the object to print is inferred from the element type of the list.
words.forEach(obj -> System.out.println(obj));
words.forEach(System.out::println);
The syntax of the bounded instance method reference where the instance method has more than one argument is no different. In the code below, the replace() method of the String class has two arguments. Its type is (String, String) -> String, the same as the function type of the target type BinaryOperator<String>. The target reference is the reference str that is defined at (4). It is effectively final when accessed in the code, and its reference value is captured at (5b) where the method reference is defined. The method replaces each occurrence of s1 in str with s2. The arguments are passed to the method when the functional method apply() of the target type BinaryOperator<String> is executed, as shown at (6).
String str = “Java Jive”; // (4)
// (String, String) -> String
BinaryOperator<String> replaceOpLE = (s1, s2) -> str.replace(s1, s2); // (5a)
BinaryOperator<String> replaceOpMR = str::replace; // (5b)
System.out.println(replaceOpMR.apply(“Jive”, “Jam”)); // (6)
// Calls str.replace(“Jive”, “Jam”) that returns the string “Java Jam”.
In a non-static context, the final references this and super can also be used as the target reference of a bounded instance method reference.
Predicate<String> p1 = s -> this.equals(s); // String -> boolean
Predicate<String> p2 = this::equals; // String -> boolean
Supplier<String> s1 = () -> super.toString(); // () -> String
Supplier<String> s2 = super::toString; // () -> String