I am trying to use Java 8 method references in my code. There are four types of method references available.
Static method reference.
Instance Method (Bound receiver).
Instance Method (UnBound receiver).
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:
What is the need for different types of method references for Instance Methods?
What is the difference between Bound and Unbound receiver method references?
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.
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
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;
}
}
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().
I've captured this from a recent presentation
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.
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]
Related
I am trying to use Java 8 method references in my code. There are four types of method references available.
Static method reference.
Instance Method (Bound receiver).
Instance Method (UnBound receiver).
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:
What is the need for different types of method references for Instance Methods?
What is the difference between Bound and Unbound receiver method references?
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.
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
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;
}
}
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().
I've captured this from a recent presentation
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.
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]
This question already has answers here:
How does method reference casting work?
(3 answers)
Closed last month.
This post was edited and submitted for review last month and failed to reopen the post:
Original close reason(s) were not resolved
I'm learning Java 8 with Lambda, Streams, and method reference. Regarding the example below,
Optional<String> s = Optional.of("test");
System.out.println(s.map(String::toUpperCase).get());
I don't understand how is it possible to use String::toUpperCase as an input for this map() method.
This is the method implementation:
public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent()) {
return empty();
} else {
return Optional.ofNullable(mapper.apply(value));
}
}
So it requires a function interface, and it has this apply() method: R apply(T t); This method has an input argument.
And toUpperCase() method doesn't have any argument:
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
If the abstract method apply(T t) has one argument, then the implemented method should have one argument of the same type. How can parameterless method toUpperCase() implement the apply(T t) method from a function interface?
I try to recreate the same conditions:
I create a functional interface:
public interface Interf {
String m1(String value);
}
Then I create a class with the method reference for m1():
public class Impl {
public String value;
public String toUpp() {
return value.toUpperCase();
}
}
And here is a class for test:
public class Test {
public static void main(String[] args) {
Interf i = String::toUpperCase;
System.out.println(i.m1("hey"));
Interf i1 = Impl::toUpp;
System.out.println(i.m1("hello"));
}
}
There isn't any issue at this statement: Interf i = String::toUpperCase; but there is a compilation error on this line: Interf i1 = Impl::toUpp;. It says:
Non-static method cannot be referenced from a static context
But toUpperCase() is also a non-static method. And even if I make the toUpp() static, it is still not working, it is working only if I add a String argument as an input argument for toUpp(). But then why is it working for String::toUpperCase?
TL;DR
Parameters that a Method reference is expected to consume according to the contract imposed by a Functional interface it implements are NOT necessarily the same as parameters of the method used in the Method reference.
This answer is a journey from this common misconception towards understanding all syntactical flavors of Method references.
Let's take tiny baby steps to dispel misunderstanding, starting from the definition of the Method reference.
What is Method reference
Here is the definition of the Method reference according to the Java Language Specification §15.13. Method Reference Expressions
A method reference expression is used to refer to the invocation of a
method without actually performing the invocation. Certain forms of
method reference expression also allow class instance creation (§15.9)
or array creation (§15.10) to be treated as if it were a method
invocation.
emphasis added
So, a Method reference is a mean of referring the method-invocation without invoking a method. Or in other words, it's a way of describing a certain behavior by delegating to the existing functionality.
Let's take a small detour and have a look at the sibling of a Method reference, a Lambda expression.
Lambda expressions are also a way to describe behavior (without executing it), and both lambdas and Method references should conform to a Functional interface. Let's declare some lambdas.
Consider, we have a domain class Foo and utility class FooUtils.
public class FooUtils {
public static Foo doSomethingWithFoo(Foo foo) {
// do something
return new Foo();
}
}
And we need to define a function of type UnaryOperator<Foo>, let's start with writing a lambda expression:
UnaryOperator<Foo> fooChanger = foo -> FooUtils.doSomethingWithFoo(foo);
Lambda receives an instance of Foo as an argument and feeds it into the existing utility method. Quite simple, right? Nothing special happens inside the lambda's body, and since have defined the type as UnaryOperator<Foo> the lambda should expect Foo. Everything is absolutely predictable, isn't it? Now the question is: can we alternate that?
Sure, we can!
UnaryOperator<Foo> fooChanger = FooUtils::doSomethingWithFoo;
That's where a Method reference comes to the rescue. It provides a shortened syntax by:
1. Dropping the lambda's arguments (they are still there, we're simply not displaying them because we know what they are).
2. Removing the parentheses after the method name. Again the method declaration is known (and let's assume that there are no ambiguities), and we are not performing any prior transformation with arguments, and we are not using any additional arguments apart from those that should come according to the contract of the Functional interface. Only in this case everything is predictable and can a method reference.
Key takeaways:
you may think of method references as if they are shortened lambdas.
the arguments of the method reference are the same the equivalent lambda receives because they are implementations to the same interface. These parameters are still there implicitly, just dropped for the purpose of conciseness. And more importantly, parameters that a method reference consumes should not be confused with parameters expected method it refers to. In other word the first parameters an input of the method reference (and they are compliant with the contract defined by the interface) and the latter related to what happens inside the reference, and have no connection to the first ones.
More examples
Let's examine a few more examples. Let's say we have a simple object representing a coin with a single property isHeads describing which side the coin is showing (i.e. heads or tails).
public static class Coin {
public static final Random RAND = new Random();
private final boolean isHeads;
public Coin() {
this.isHeads = RAND.nextBoolean();
}
private Coin(boolean isHeads) {
this.isHeads = isHeads;
}
public Coin reverse() {
return new Coin(!isHeads);
}
public boolean isHeads() {
return isHeads;
}
}
Let's generate a coin. For that we can use implement of Supplier which very generous, supplier doesn't receive arguments, it produces a value. Let's definable both a lambda and a reference
Supplier<Coin> coinProducer = () -> new Coin(); // no argument required according to the contract of Supplier
Supplier<Coin> coinProducer1 = Coin::new; // Supplier expressed as a reference to no-args constructor
Both don't receive any argument (as per contract of the Supplier), both refer to the no-arguments constructor.
Now let's consider the predicates determining if the coin shows heads implemented via a lambda and a method reference:
Predicate<Coin> isHeads = coin -> coin.isHeads();
Predicate<Coin> isHeads1 = Coin::isHeads;
Again, both the lambda and the method reference are compliant with the Predicate's contract and both receive an instance of Coin as an argument (it can't be otherwise, simply concise syntax of the method reference doesn't show that).
So far, so good? Let's move further and try another way to obtain a Coin, let's define a Function:
Function<Boolean, Coin> booleanToCoin = value -> new Coin(value);
Function<Boolean, Coin> booleanToCoin1 = Coin::new;
Now both the lambda and the reference are consuming a boolean value and making use of the parameterized constructor. Did not notice that method reference describing Supplier<Coin> and Function<Boolean, Coin> looks identical.
Reminder: both Lambda expressions and Method references have no type by itself. They are so-called poly-expressions, which means their type should be inferred by the compiler based on the context in which they appear. Both the lambda and the reference should conform to a Functional interface, and the interface they implement dictates who they are and what they are doing.
In all examples described earlier, arguments of consumed by a method reference appeared to be the same as the ones expected by the referenced method, but it's not mandatory for them to be the identical. It's time to examine a couple or examples where it not the case to dispel the illusions.
Let's consider a UnaryOperator reversing a coin:
UnaryOperator<Coin> coinFlipper = coin -> coin.reverse(); // UnaryOperator requires one argument
UnaryOperator<Coin> coinFlipper1 = Coin::reverse; // UnaryOperator still requires one argument expressed as a reference to no arg method
All implementations of the UnaryOperator receive a Coin instance as an argument, and another coin is being produced as a result of the invocation of reverse(). The fact that reverse is parameterless is not an issue, because we concerned about what it produces, and not what it consumes.
Let's try to define a tougher method reference. To begin with, introduce in the Coin class a new instance method called xor(), which is immensely useful for XOR-ing two coins:
public Coin xor(Coin other) {
return new Coin(isHeads ^ other.isHeads);
}
Now when two object come into play we have more possibilities, let's start with the simplest case one by defining a UnariOperator:
final Coin baseCoin = new Coin();
UnaryOperator<Coin> xorAgainstBase = coin -> baseCoin.xor(coin);
UnaryOperator<Coin> xorAgainstBase1 = baseCoin::xor;
In the above example an instance of Coin defined outside the function is used to perform the transformation via the instance-method.
A little bit more complicated case would be a BinaryOperator for XOR-ing a couple of coins might look like this:
BinaryOperator<Coin> xor = (coin1, coin2) -> coin1.xor(coin2);
BinaryOperator<Coin> xor1 = Coin::xor;
Now we have two arguments coming as an input and a Coin instance should be produce as an output as per BinaryOperators contract.
The interesting thing is the first argument serves as an instance on which the method xor() would be invoked, and the second is passed to the method (note that xor() expect only one argument).
You might ask what would happen if there would be another method for XOR-ing coins. A static method expecting two arguments:
public static Coin xor(Coin coin1, Coin coin2) {
return new Coin(coin1.isHeads ^ coin2.isHeads);
}
Then the compiler would fail to resolve the method reference, because here we have more the one potentially applicable method and none of them can be considered to be more specific than the other since the types of arguments are the same. That would cause a compilation error. But if we would have either of them (not both together), reference Coin::xor would work fine.
Types of Method references
Basically, the examples that we have walked through covered all the types of method references. Now, let's enumerate them.
The official tutorial provided by Oracle re are four kinds of method references:
Reference to a Static method
Class::staticMethod
Example Coin::xor which refers to the static method xor(Coin coin1, Coin coin2).
Examples with standard JDK-classes:
BinaryOperator<Integer> sum = Integer::sum; // (i1, i2) -> Integer.sum(i1, i2)
BiFunction<CharSequence, Iterable<CharSequence>, String> iterableToString
= String::join; // (delimiter, strings) -> String.join(delimiter, strings)
Reference to an instance method of a particular object
instance::instanceMethod
The example illustrating this case would the usage of the instance method xor(Coin other) with a coin defined outside the function, which is internaly used to invoke xor() on it passing the function-argument into the method.
final Coin baseCoin = new Coin();
UnaryOperator<Coin> xorAgainstBase1 = baseCoin::xor; // same as coin -> baseCoin.xor(coin)
Examples with standard JDK-classes:
Set<Foo> fooSet = // initialing the Set
Predicate<Foo> isPresentInFooSet = fooSet::contains;
Reference to an Instance Method of an Arbitrary Object of a Particular Type
Class::methodName
In this case method refernce operates on an instance that comes as an argument (we would have reference to it only it we would use a lambda), therefore containing type, which can be tha actual type or one the super types, is used to refer to this instance.
An example would a Predicate checking if the coin shows heads Coin::isHeads.
Examples with standard JDK-classes:
Function<List<String>, Stream<String>> toStream = Collection::stream;
List<List<String>> lists = List.of(List.of("a", "b", "c"), List.of("x", "y", "z"));
List<String> strings1 = lists.stream()
.flatMap(toStream)
.toList();
// A slightly more complicate example taken from the linked tutorial
// Equivalent lambda: (a, b) -> a.compareToIgnoreCase(b)
String[] stringArray = { "Barbara", "James", "Mary" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
Reference to a Constructor
Class::new
We have cove this case already with the following examples:
Supplier<Coin> refering to no args-constracor implemented as Coin::new;
Function<Boolean, Coin> which makes use of the single-arg constructor by passing incoming boolean value also expressed as Coin::new.
How can toUpperCase() method implement apply(T t) method from Function
interface?
The method reference String::toUpperCase has unbound receiver.
Java 8: Difference between method reference Bound Receiver and UnBound Receiver
Then the argument T t would be the receiver
In your example
public <U, T> Optional<U> map(Function<? super T, ? extends U> mapper) {
...
return Optional.ofNullable(mapper.apply(value));
}
by calling map(String::toUpperCase)
If value, let's say equal to "Example String", would be the receiver so mapper.apply("Example String"); would be equivalent to "Example String".toUpperCase();
public class X{
public static void print(Integer n, Function<Integer, String> fn) {
System.out.println(fn.apply(n));
}
public static void main(String []args){
print(3, Integer::repeat()); //
}
}
Example:
Since 3 is a given number, the value returned by the function and printed on the console should be "aaa".
Your method reference is wrong in a couple ways. First, a method reference doesn't end in parentheses (). Second, the Integer class doesn't have any repeat method.
The String class defines the repeat method, available for the first time in Java 11. But you want to execute on a specific instance of a string, namely the constant "a".
Try "a"::repeat.
Method references as well as lambda expressions should conform to a function interface, by providing an implementation of the abstract method defined by this interface.
And interface Function declares a method apply()
R apply(T t);
You can implement it like that with lambda expression:
Function<Integer, String> fun = num -> String.valueOf(num);
Method repeat() is an instance method of the String class, i.e. you have to invoke it on the instance of the String. So, need to define this string either by outside the lambda or hard-code it inside the body of lambda.
public static final String str = "a";
Function<Integer, String> fun = num -> str.repeat(num);
Now, in order to transform this lambda expression in a method reference, let's first pose the question of what the method reference is.
Method reference - is the way to utilize an existing method to provide an implantation of the behavior defined by a function interface.
There are four kinds of method references (a quote from the Oracles's tutorial):
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new
Note that the syntax of method references doesn't require parentheses () because that how the language was designed, and I guess because method references are intended to be concise and expressive.
The type of method reference that we need to implement this function using repeat() method is a reference to an instance method of a particular object (because repeat() is an instance method). And that's how it might look like:
public static final String str = "a";
Function<Integer, String> fun = str::repeat;
print(3, fun); // or print(3, str::repeat);
Note that variables used inside both method references and lambda expressions must be final or effectively final, i.e. should be assigned only once.
This question already has answers here:
Java 8: Difference between method reference Bound Receiver and UnBound Receiver
(6 answers)
Closed 5 years ago.
I read the tutorial for Reference to an instance method of an arbitrary object of a particular type . Here is the link https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
So I wrote my own code but I am unable to remove error .
Arrays.sort(arr, String::compareToIgnoreCase); is a legal statement
but
Consumer m2 =Myclass::doit; gives an error .
How to resolve this error ? Is the concept of Reference to an instance method of an arbitrary object of a particular type applicable to inbuilt classes of java like String ?
Here goes my code
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
interface Myinter {
void doit(List<String> s);
}
public class Myclass implements Myinter {
public static void main(String[] args) {
List<String> obj = new ArrayList<String>();
obj.add("raj");
obj.add("gopal");
obj.add("bhallamudi");
String arr[] = new String[obj.size()];
arr = obj.toArray(arr);
Arrays.sort(arr, String::compareToIgnoreCase);
for (String s : arr)
System.out.println(s + " ");
Consumer<List<String>> m2 =Myclass::doit;
m2.accept(obj);
}
#Override
public void doit(List<String> s) {
System.out.println(s);
}
}
When you create an unbound reference to an instance method, the receiver instance becomes the first functional argument.
In the case of String::compareToIgnoreCase, you will have two string arguments being passed to Comparator.compare; the method compareToIgnoreCase will be invoked on the first getting the second passed as argument.
Likewise, you would have to use
BiConsumer<Myclass,List<String>> m2 = Myclass::doit;
m2.accept(new MyClass(), obj);
as you can’t invoke the instance method doit without an instance of Myclass. Or, incorporating your interface:
BiConsumer<Myinter,List<String>> m2 = Myinter::doit;
m2.accept(new MyClass(), obj);
Here, an instance of any class implementing Myinter is sufficient.
The same rules for applying method references apply to standard Java classes like String and also your own classes. In your example, you have 2 types of usages of method references. 1) Instead of normal invocation doit and 2) Replacing a lambda expression.
The notation in Java 8 Myclass::doit means you are calling the static method doit on Myclass. Instead you have to create an object for Myclass and call it on your object as follows:
Myclass reallyMyClass = new Myclass();
Consumer<List<String>> m2 = reallyMyClass::doit;
m2.accept(obj);
When using method references to replace a lambda, the notation is slightly different and references can be used using the name of the class. So the usage with String in your example when expanded means:
Arrays.sort(arr, (s1, str) -> s1.compareToIgnoreCase(str));
and the shorthand version of that replaces the definitions of the lambda parameters and calls the method on s1 using parameter str as the argument to compareToIgnoreCase hence having this:
Arrays.sort(arr, String::compareToIgnoreCase);
For further reference, this blog post is a good read on this subject: https://www.codementor.io/eh3rrera/using-java-8-method-reference-du10866vx
I am trying to use Java 8 method references in my code. There are four types of method references available.
Static method reference.
Instance Method (Bound receiver).
Instance Method (UnBound receiver).
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:
What is the need for different types of method references for Instance Methods?
What is the difference between Bound and Unbound receiver method references?
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.
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
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;
}
}
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().
I've captured this from a recent presentation
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.
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]