Make code clearer with method references
Lambda expressions allow you to use a code as data and pass it as a method's arguments. Another way to use the code as data is writing method references. Often, the method references are clearer and readable than corresponding lambda expressions.
For example, sometimes, a lambda expression is just a call to a method:
Consumer<String> printer = str -> System.out.println(str);
To make this code clearer we can use a reference to a method by its name:
Consumer<String> printer = System.out::println;
The Consumer is a functional interface with a single abstract method accept which takes a value of type T and returns nothing.
The base syntax of a method reference looks like:
something :: methodName
where something can be a class name or a particular instance.
To use a method reference you need a functional interface (with a single abstract method) as well as for a lambda expression.
Kinds of method references
It's possible to write method references to static and instance (non-static) methods.
There are four kinds of method references:
- reference to a static method;
- reference to an instance method of an existing object;
- reference to an instance method of an object of a particular type;
- reference to a constructor.
1) Reference to a static method
The general form:
ClassName :: staticMethodName
Here is an example - a reference to the static method sqrt of the class Math.
Function<Double, Double> sqrt = Math::sqrt;
Now, we can invoke the sqrt function for double values:
sqrt.apply(100.0d); // the result is 10.0d
Of course, the function sqrt can be written using the following lambda expression:
Function<Double, Double> sqrt = x -> Math.sqrt(x);
2) Reference to an instance (non-static) method of an existing object
The general form:
objectName :: instanceMethodName
Here is an example - a reference to the method nextLine of the class Scanner.
Scanner scanner = new Scanner(System.in); // IO scanner
Supplier<String> lineReader = scanner::nextLine; // method reference
Note, Supplier<String> is a functional interface with the single abstract method get which takes no arguments and returns a result of the String type.
To get lines from the standard input let's write the following code:
String firstLine = lineReader.get();
String secondLine = lineReader.get();
The same lineReader can be written using a lambda expression:
Supplier<String> lineReader = () -> scanner.nextLine(); // less readable than the reference
3) Reference to an instance method of an object of a particular type
The general form:
ClassName :: instanceMethodName
Here is an example - a reference to an instance the method doubleValue of the class Long.
Function<Long, Double> converter = Long::doubleValue;
Now, we can invoke the converter for long values:
converter.apply(100L); // the result is 100.0d
converter.apply(200L); // the result is 200.0d
Also, we can write the same converter using the following lambda expression:
Function<Long, Double> converter = val -> val.doubleValue();
4) Reference to a constructor
The general form:
ClassName :: new
Here is an example - the reference to the constructor of the String class:
Supplier<String> generator = String::new;
As you can see, the method reference is more readable than the same lambda expression
Supplier<String> generator = () -> new String(); // less readable than the method reference
Further, we will use lambda expressions and method references together.