Method References
Goals
- Learn about method references.
- Experiment with using method references in functions, higher-order functions, and internal iteration
Concepts
- method reference
Language
::
::new
super::
this::
Library
java.util.Iterable.forEach(Consumer<? super T> action)
java.lang.String.isEmpty()
java.lang.Collection.removeIf(Predicate<? super E> filter)
java.util.Comparator<T>
java.util.Comparator.compare(T object1, T object2)
java.util.Comparator.comparing(Function<? super T,? extends U> keyExtractor)
java.util.Comparator.comparingInt(ToIntFunction<? super T> keyExtractor)
java.util.List.sort(Comparator<? super E> comparator)
java.util.function.Consumer<T>
java.util.function.Predicate<T>
Preview
final List<Point> points = getPoints();
points.sort(comparing(point::getX));
Lesson
In the previous lesson you learned how lambda expressions can facilitate implementing single-method functional interfaces. You looked at an example ofScene
for holding points, initialized with a Plotter<Point>
strategy for placing the points on some two-dimensional plane. A lambda expression allows implementation of Plotter<Point>
without all the boilerplate usually associated with anonymous classes.
Method References
If you do nothing more in the body of your lambda expression than call a single method with the same parameters as the lambda, as in the example above, Java provides an even shorter representation called a method reference. Although Java does not really allow a true reference to a method, the method reference
syntax will produce the equivalent of a lambda that calls the method you indicate. A method reference consists of the object or class on which the method will be invoked, followed by two colon ::
characters, followed by the name of the method.
Instance Method References
Remembering from above that a Scene
constructor takes a functional interface with a single method Plotter.plot(Point)
, the lambda expression above is equivalent to the method reference System.out::println
, referring to the println()
method of the System.out
instance:
You can refer to the appropriate instance method of any object you have a reference too. If it is not evident from the above example that System.out::println
is an instance method reference, imagine that a class Voice
has a method Voice.say(Point)
. If you have a reference to a Voice
you could pass it to the scene as a way to plot
each point.
Static Method References
Method references to static methods are just as straightforward; simply use the name of the class that has the static method instead of the instance variable that has the instance method.
Let's say that you have a general ConsolePresentation
class of static utilities for outputting things to the console. You could pass a static reference to ConsolePresentation::printPoint
to the Scene
constructor as an implementation of Plotter<Point>
Type Method References
You can also refer to an instance method of one of the functional interface parameters by using class representing the object's type.
Maybe the Scene that we've been using has a method to print all the points it knows about. The Scene.printAllPoints(PointLabelGenerator)
method takes a strategy for generating a label for points:
We could then ask a scene to print out all the points using an anonymous class to implement PointLabelGenerator
.
With lambdas that becomes much more readable:
But because the lambda simply invokes a method on the in the parameter, we can simply pass a method reference and make things even clearer:
A method reference to an instance method of the parameter object can have another twist: it can call a method on one parameter and use the other parameter(s) as arguments to the method call! Imagine that the scene point printing method actually has two parameters, with the second being a strategy for grouping points in the output: Scene.printAllPoints(PointLabelGenerator, PointGrouper)
. The PointGrouper
strategy is a functional interface for indicating whether two points should be grouped together:
Furthermore let's say that the Point
class has a method for determining if a point is in the same quadrant as another point. (That is, each of the X and Y coordinates has the same sign as that of the other point.)
Scene.printAllPoints(PointLabelGenerator, PointGrouper)
method like this:
With a lambda expression, of course, that becomes much more compact:
Again this lambda is only a method call—but notice that the method call is invoked on the first lambda parameter, passing in the second lambda parameter as an argument. We can still replace this with a method reference!
Constructor References
You can even reference a constructor using the syntax ::new
. If a method calls for a functional interface that produces a Foo, you can pass in the constructor reference Foo::new
, if its parameters match the given parameters (or a default constructor if no other parameters are given). This even works with arrays, such as int[]::new
.
Method References in Functions
When exploring lambda expressions, you learned that Java provides a number of useful functional interfaces in thejava.util.function
package. One of them, java.util.function.Predicate<T>
, defines a strategy for testing an object in some way and returning a boolean
result. You discovered how to remove items matching a Predicate<T>
from a collection by calling the Collection.removeIf(Predicate<? super E> filter)
and passing a lambda:
Remember that if you have an existing method that matches the arguments of the functional interface method, you could use a method reference instead of a lambda. What if you wanted to remove all empty strings from a list? You could use the same Collection.removeIf(Predicate<? super E> filter)
method, calling the String.isEmpty()
method in the lambda expression representing the Predicate<T>
:
Because String.isEmpty()
does not have any extra parameters (unlike String.startsWith(String)
), you could use a method reference to String::isEmpty
to make your code even more concise. The String
part of String::isEmpty
would refer to the functional interface String
parameter, as is evident in the lambda expression above..
Higher-Order Functions
You can just as easily use method references for passing functions to higher-order function methods. In a previous lesson you used the static method Comparator<T> Comparator.comparing(Function<? super T,? extends U> keyExtractor)
to produce a Comparator<T>
. When calling List.sort(Comparator<? super E> comparator)
you passed in a Function<T, R>
that determines the comparison value for an object, using a lambda expression to implement the Function<T, R>
interface:
We can do even better than that using a method reference to Point.getX()
. For added simplicity we can statically import Comparator.comparing(…)
so that the entire statement flows like a sentence.
Internal Iteration
Internal iteration via the Iterable.forEach(Consumer<? super T> action)
can also be made more concise with method references. In a previous lesson you learned how to used a lambda expression to simplify the implementation of java.util.function.Consumer<T>
when calling Iterable.forEach(…)
.
Because the lambda in the above example simply delegates to the method System.out.println(…)
with a single argument, the entire expression can be replaced with a method reference to System.out::println
.
Review
Summary
Common forms of method references:
Form | Example | Description |
---|---|---|
object::method | fooBar::doSomething | Reference to an instance method of an object. Can also be used with this:: or super:: . |
Class::method | Utilities::doSomething | Reference to a static method of a class. |
Type::method | FooBar::doSomething | Reference to a method of one of the functional interface parameters. |
Class::new | FooBar::new | Reference to a class constructor. |
Think About It
- If you don't know where to start when using a method reference, try first using a lambda expression and see if it consists of no more than a method call. If there are no additional parameters used by the method not related to the functional interface parameters, converting the lamda into a method reference is usually straightforward.
Task
You have already created a Booker.listPublications(…)
method for display of publications in the presentation layer. So far this method has been specific to the command-line interface. You decide that you you want to make the division between the presentation concern and the business concern more evident by extracting the logic for printing publication to stdout
into a separate method from the general publication listing method.
- Create a separate
printPublication(Publication)
method for printing an individual publication to the standard output. - When using internal iteration inside
listPublications(…)
, instead of providing a lambda for theConsumer<T>
parameter, indicate that theprintPublication(Publication)
method will be the strategy for listing each publication.
Use method references in your comparator strategies wherever possible.