Method References (JSR 335)
In the Java language a method reference allows converting a specific method to an instance of a functional interface. When the reference is evaluated the method is not yet called. The created functional interface instance can be used to invoke the method at a later point in time. Example:
The formal parameters of the method defined by the functional interface has to match the signature of the method reference.
With method references functional interfaces can be created without using the lambda syntax. As this results in a more concise notation it is considered a good practice to prefer method references over lambdas. For example a boolean property can be directly used as a predicate:
var activeUsers = allUsers.stream().filter(User::isActive).collect(toList);
Syntax
Java uses two colons ::
to denote a method reference:
<Type or Instance>::<method identifier>
Other languages like JavaScript do not have a specific syntax for method references. Methods are simply slots of objects like fields and can be referenced with the regular dot without parenthesis to trigger a call:
// method object in JavaScript
runmethod = worker.run
runmethod()
As the Java language allows fields and methods with the same name this would result in ambiguities. Therefore a new distinct syntax using two colons ::
was required.
References to static methods
The simplest case for method references are static methods. In this case the signature of the static method must be the same as the signature of the abstract method defined by the functional interface:
References to unbound instance methods
If you create a reference to an instance method the situation becomes more tricky. In the next example we make a direct reference to the method of a certain type. So the method is unbound which means that at the point in time when we call the method an instance to call the method on must be provided.
When we for example want to call String.length()
we need to provide a String
instance. The compatible functional interface needs to define the method references’ type as the first parameter:
This still works if the instance method itself has parameters. In this case the first parameter of the method in the functional interface represents the instance we want to invoke the referenced method on, the remaining parameters will be mapped to the method parameters.
References to bound instance methods
The method reference syntax ::
cannot only be used with types for instance methods it can also be used on instances. In this case the method is bound to the given instance at the point in time when the functional interface is created.
Note that the instance is bound at the point in time when the method reference is created. In this example the printer
will still print to the original output even if you change the default output with System.setOut()
. This is a
subtle difference to the following similar lambda which evaluates the value of System.out
at every invocation:
Consumer<String> printer = s -> System.out.println(s);
In the context of a class instance this
as well as super
can be used to to reference methods of the current instance:
References to constructors
Constructors can be referenced by using new
as an identifier. The new
method reference works on
- top level classes,
- inner classes,
- local classes and
- array types.
In case of non-static inner classes the enclosing instance is evaluated when the method reference is created. This example demonstrates where new
references are applicable:
Parameterized types and methods
Method references can come with type parameters. Both for the referenced type and method type parameters can be provided if the underlying APIs declare them:
// parameterized type
Supplier<List<String>> listFactory = ArrayList<String>::new;
// parameterized method
Supplier<Comparator<Integer>> cmpFactory = Comparator::<Integer>naturalOrder;
Things that do not work
While at most places references can be used instead of direct method or constructor invocations there are situations where method references will not result in valid Java source code.
For example there is no method reference syntax to create an inner class for a given enclosing outer class instance like with this direct invocation:
Inner i = outer.new Inner();
If this is required the only alternative is to fall back to lambda syntax:
Supplier<Inner> = () -> outer.new Inner();
Using a type parameter for the reference type itself is not possible:
// does not compile
Supplier<T> creator = T::new;
Literals other than string are no valid types for method references. There is no outoboxing for primitive literals:
// does not compile
ToIntFunction<Integer> cmp42 = 42::compareTo;
The compiler needs to know the functional interface the method reference must be converted to. Therefore method references cannot be used to initialize local variables declared with var
:
// does not compile
var printer = System.out::println;;