Static Method References – Functional-Style Programming

Static Method References

Sometimes the lambda body of a lambda expression is just a call to a static method. The lambda expression at (1a) calls the static method now() in the class java.time.LocalDate to obtain the current date from the system clock.

Click here to view code image

Supplier<LocalDate> dateNowLE = () -> LocalDate.now();  // (1a) Lambda expression

The lambda expression has the type () -> LocalDate, and not surprisingly, it is also the type of the static method now() which takes no arguments and returns an instance of the LocalDate class. The method type is compatible with the function type of the target type—that is, it is compatible with the type of the functional method get() of the parameterized functional interface Supplier<LocalDate>. The compiler can infer from the context that the type of the static method is compatible with the target type of the functional interface. In such a case, the lambda expression can be replaced by a static method reference:

Click here to view code image

Supplier<LocalDate> dateNowMR = LocalDate::now;         // (1b) Method reference

The double-colon delimiter (::) separates the reference type name (class LocalDate) from the static method name (now) in the syntax of the static method reference.

Analogous to a lambda expression, a method reference can be used as a value in an assignment, passed as an argument in a method or constructor call, returned in a return statement, or cast with the cast operator (p. 733). Its execution is deferred until the functional method of its target type is invoked, as at (2).

Click here to view code image

LocalDate today = datenowMR.get();    // (2) Method reference at (1b) executed.
System.out.println(today.format(DateTimeFormatter.ISO_DATE)); // 2021-03-01

The following rule can be helpful in converting between a lambda expression and a static method reference:

A lambda expression of the form

Click here to view code image

(args) -> RefType.staticMethod(args)

is semantically equivalent to the static method reference:

Click here to view code image

RefType::staticMethod

where RefType is the name of a class, an enum type, or an interface that defines the static method whose name staticMethod is specified after the double-colon (::) delimiter.

Arguments are generally not specified in the method reference, and any parameters required by the method are determined by the target type of the context.

Click here to view code image

// String -> Integer
Function<String, Integer> strToIntLE = s -> Integer.parseInt(s); // (3a)
Function<String, Integer> strToIntMR = Integer::parseInt;        // (3b)
System.out.println(strToIntMR.apply(“100”));                     // (4)
// Calls Integer.parseInt(“100”) that returns the int value 100 which is boxed
// into an Integer.

The static method Integer.parseInt() in the lambda body at (3a) takes one argument. Its type is String -> Integer. Using the one-arity Function<String, Integer> as the target type is appropriate as its function type is also String -> Integer. We can convert the lambda expression at (3a) to the static method reference shown at (3b). Note that no arguments are specified at (3b). The argument is passed to the static method at a later time when the functional method apply() of the functional interface is invoked, as demonstrated at (4).

Similarly, we can define static method references whose static method takes two arguments. A two-arity function or a binary operator from the java.util.function package can readily serve as the target type, as demonstrated by the following examples. Note that the static method Math.min() is overloaded, but the target type of the context determines which method will be executed. The type of the method min() at (5) is different than the type at (6). Analogous to lambda expressions, the same method reference can have different target types depending on the context.

Click here to view code image

// (double, double) -> double
DoubleBinaryOperator minDoubleLE = (x, y) -> Math.min(x, y);
DoubleBinaryOperator minDoubleMR = Math::min;          // (5)
System.out.println(minDoubleMR.applyAsDouble(20.0, 30.0));
// Calls Math.min(20.0, 30.0) that returns the double value 20.0.
// (int, int) -> (int)
IntBinaryOperator minIntLE = (x, y) -> Math.min(y, y);
IntBinaryOperator minIntMR = Math::min;                // (6)
System.out.println(minIntMR.applyAsInt(20, 30));
// Calls Math.min(20, 30) that returns the int value 20.

If the static method requires more that than two arguments, we can either define new functional interfaces with the appropriate arity for their functional method, or use the currying technique (p. 723).

Leave a Comment