Method References in Practice
A method reference is a shorthand for a lambda expression that invokes one specific method.
Two Variables, One Interface
Imagine two variables that both hold an object of the same functional interface, but one is written in a long form and the other in a short form. When a program prints something unexpected, the first question to ask is which expression produced the value each variable holds. This article is about reading that shorthand, the method reference, and tracing where its value comes from.
The interface used as the running example is SupplyInt. Its one abstract method is get(), which takes no arguments and returns an int. Any lambda or method reference assigned to a SupplyInt variable must have that same shape. Keep this interface in mind, because the rest of the article uses it to show when a shorthand fits and when it does not.
The source material for this article begins with running code rather than with a definition. The question to hold onto as you read is simple: for each variable, which expression is stored in it, and which method actually runs when get() is called on it?
Reading the Double-Colon Notation
A method reference is a shorthand for a lambda expression that invokes one specific method. It is written as a target, followed by a double colon, followed by a method name. Notice what is missing: there are no parentheses and no argument list after the method name. The expression names the method without calling it at the point where it appears.
The target before the double colon determines which form you are reading. Read the expression from left to right. The part before the double colon names the class or the object, the double colon marks the method reference, and the part after it names the method. In Math::sqrt, the class is Math and the method is sqrt. Together they say: use the sqrt method from the Math class.
From Lambda to Method Reference
A lambda expression is an anonymous function, meaning a function written inline without a separate name. When the body of a lambda does nothing except call an existing method, the lambda can be rewritten as a method reference. The reference names the method directly and leaves out the parameter list and the call syntax that the lambda spelled out.
s -> s.length()A lambda takes a parameter s and calls s.length() in its body.
String::lengthA method reference names the length method directly, with no parameter list and no call syntax.
Both expressions say the same thing about the method being used, and both can fill the same functional interface slot when their shapes match. What the rewrite drops is the explicit parameter name and the explicit call. What it keeps is the method itself. The method reference is therefore a shorter way to write a lambda whose whole job is one method call.
A method reference is not a different kind of value from a lambda in the context of an interface. It is a shorter way of writing one specific kind of lambda, the kind that only invokes an existing method.
Matching the Interface Signature
A method reference is valid only when the functional interface's single abstract method matches the referenced method. The source describes a functional interface as an interface with a single abstract method, and it names the referenced method must match it. In practice this means the parameters and the return type of the interface method must line up with the parameters and return type of the method being referenced.
The figure shows the matching for SupplyInt. Its get() takes no arguments and returns an int. The source notes that str::length matches this shape, because length() takes no arguments and returns an int. The reference therefore fits into the single slot that SupplyInt provides. The receiver str is not an argument of the interface method. It is the object fixed in the reference itself, so it does not change the shape check.
Tracing Math::sqrt Through a Method Call
The source gives a line in which the method reference Math::sqrt is passed to a method named sum, together with the integers 1 and 100. The line also prints a message first and then prints the value that sum returns. The code below mirrors the shape of that line. The body of sum is not part of the excerpt, so this trace follows only what the call sites show.
- Math::sqrt is passed as an argument. It names the sqrt method and does not call it here.
- 1 and 100 are the other two arguments to sum.
- The value returned by sum is what println displays.
- 1Evaluate the arguments of sum from left to right: the method reference Math::sqrt, then the integer 1, then the integer 100.
- 2Call sum with those three values. Inside sum, the parameter that received Math::sqrt is a functional interface, and the call to its single abstract method is where sqrt actually runs.
- 3sum returns a value, which is the value produced by the referenced method.
- 4
System.out.printlnreceives that returned value and displays it on the same line as the earlier printed text.
The diagram follows the value from the place where Math::sqrt is written to the place where the printed number is produced. The reference itself does not produce a value. It is handed to sum, and the value is produced only when the interface method is invoked inside sum. That is why the reference is written without parentheses: writing sqrt() there would call the method at the wrong time and pass its result instead of the method.
Locating an Unexpected Result
When a program prints something other than what you expected, do not start by re-reading the whole program. Work through the chain in order: which interface was chosen, how the parameters were bound, which method actually ran, and what it returned. The source suggests tracing the two variables one at a time, finding the line where each is assigned and identifying which expression it holds.
The source gives a useful split. If two variables that are objects of the same interface disagree, the difference must come from their assignments, because the interface is the same for both. If both agree but are wrong, the problem is more likely inside the referenced method itself. This narrows the search before you read any method body.
Mistakes Beginners Make
Common mistake Adding parentheses after the method name in a method reference
What it looks likeMath::sqrt() written where Math::sqrt was intendedWhy it failsParentheses turn the expression into a call that runs immediately, not a reference that names the method.
Do this insteadWrite the reference without parentheses and let the functional interface's method call invoke it later.
Common mistake Assuming the reference fits any interface
What it looks likeUsing str::length where the target interface's method takes an argument or returns a non-int value
Why it failsA method reference is valid only when the interface's single abstract method matches the referenced method's shape.
Do this insteadCompare the interface method's parameters and return type with the referenced method before assigning the reference.
Common mistake Forgetting that the object::method form fixes the object at the point of writing
Why it failsThe object before the double colon is the instance the method is called on, so reading it as a class name leads to a wrong reading of which method runs.
Common mistake Reading the double colon as a call rather than as a reference to a method that will be invoked later
Practice Tracing a Method Reference
Using the sum line from earlier, Math::sqrt is replaced by a different reference that names an existing method of the same shape. Without running the code, trace the argument list in order, then name the step that produces the value that println prints. Explain what would change if you added parentheses after the method name.
Hints
- Start by identifying the class or object before the double colon and the method after it.
- The value is produced when the interface method is invoked inside sum, not when the reference is written.
- Parentheses would make the expression a call instead of a reference.
Key Takeaways
- A method reference such as Math::sqrt is a shorthand for a lambda expression that invokes one specific method, written without parentheses or an argument list.
- The Class::
staticMethodform names a static method of a class, while the object::method form names an instance method on an existing object. - A method reference is valid only when the target functional interface's single abstract method matches the referenced method's parameters and return type.
- When tracing a line with a method reference, follow the arguments first, then the call that invokes the interface method, then the returned value that is printed.