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);
Java uses two colons
:: to denote a method reference:
<Type or Instance>::<method identifier>
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 a 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 a 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
// does not compile var printer = System.out::println;;