Motivation
According to Oracle's the Java tutorials:
You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.
I am aware of method references and thought that they are what they are: a more succinct way of referring to methods without providing explicit information that the compiler already know. What I did not realize was that method references can be categorized into four different types (summary below). Among the four types, 3 of them are fairly easy to understand:
- Call to a constructor by doing
ClassName::new
instead of() -> new ClassName()
- Call to a static method by doing
ClassName::methodName
instead of() -> ClassName.methodName()
- Call to an instance method of a particular object by doing
ObjectName::methodName
instead of() -> obj.methodName()
What intrigued me was the four conditions: call to an instance method of a particular class. In that case, will JVM be creating an instance of that class and then invoking the instance method via the newly created instance?
Summary table
Type (Reference to) | Syntax | Example | Lambda Equivalent |
---|---|---|---|
a static method | ContainingClass::staticMethodName | Integer::parseInt | str -> Integer.parseInt(str) |
an instance method of a particular object | containingObject::instanceMethodName | Instant.now()::isAfter | Instant then = Instant.now(); t -> then.isAfter(t) |
an instance method of an arbitrary object of a particular type | ContainingType::methodName | String::toLowerCase | str -> str.toLowerCase() |
a constructor | ClassName::new | int[]::new HashMap::new | len -> new int[len]; () -> new HashMap() |
Note: based on summaries from Oracle's the Java tutorials and Effective Java
Image:
The hunt
After some Googling, I found a few articles about method references. So there are in fact a few ways we can do a method reference to an instance method without specifying the instance in the method reference.
Examples:
// Method references and their equivalent lambda expressions
String::toLowerCase // (a) -> a.toLowerCase()
String::compareToIgnoreCase // (a, b) -> a.compareToIgnoreCase(b)
Integer::compareTo // (a, b) -> a.compareTo(b)
In the above cases, it can be seen that the instance method is going to be invoked on the instance that is passed in as the first parameter. So JVM did not magically create an instance just to invoke an instance method on the specified class.
So in fact, this is a case of bound vs unbound.
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
In my own words:
- Bounded means we specify the object that we are going to call the instance method on.
- Unbounded means we specify that the instance method is going to be called from the first parameter that is passed in, so the first parameter is the object.
Thoughts
The concept of method references is not difficult. I thought it is interesting to write about it because of the process of how I came to learn more about them.
After some initial research, I came across this article which says the following:
String::toLowerCase
is an unbound non-static method reference that identifies the non-staticString toLowerCase()
method of theString
class. However, because a non-static method still requires a receiver object (in this example aString
object, which is used to invoketoLowerCase()
via the method reference), the receiver object is created by the virtual machine.toLowerCase()
will be invoked on this object.
The receiver object is created by the virtual machine? I thought to myself: if JVM is going to create an object of the class specified in the method reference in order to call the instance method, how does it know which constructor to invoke? If it simply calls the default constructor, i.e. new ClassName(), what about classes that do not have such a parameterless constructor?
So I opened up jshell and typed in a bunch of classes to verify it:
// driver
class Test {
static void print(Function<String, String> fn, String s) {
System.out.println(fn.apply(s));
}
}
// test 1
class MyString {
String toLowerCase(String s) {
return s.toLowerCase();
}
}
// test 2
class MyOtherString {
int one;
MyOtherString(int one) {
this.one = one;
}
String toLowerCase(String s) {
return s.toLowerCase();
}
}
// test
Test.print(String::toLowerCase, "HELLO WORLD");
Test.print(MyString::toLowerCase, "HELLO WORLD");
Test.print(MyOtherString::toLowerCase, "HELLO WORLD");
The error messages for my last two test cases are as follows:
| Error:
| incompatible types: invalid method reference
| unexpected instance method toLowerCase(java.lang.String) found in unbound lookup
Whether my custom classes have a parameterless constructor or not, both calls did not succeed. So it was not a case whereby a magical object of MyString
or MyOtherString
will be created and does something like magicInstance.toLowerCase("HELLO WORLD)"
.
To end this little piece of article off, I thought of the quote by Albert Einstein:
“Anyone who has never made a mistake has never tried anything new.”
The process of experimenting with something new or unfamiliar is indeed a great way to learn. At least now I know more about method references and have one article written about it:)
References
- https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
- https://www.baeldung.com/java-method-references
- https://www.infoworld.com/article/3453296/get-started-with-method-references-in-java.html
- https://stackoverflow.com/questions/56453749/unexpected-instance-method-function-found-in-unbound-lookup
- https://stackoverflow.com/questions/35914775/java-8-difference-between-method-reference-bound-receiver-and-unbound-receiver/35915104
- https://www.youtube.com/watch?v=hSfylUXhpkA&t=833s
Top comments (0)