JavaFunctional streams

Method references

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.
How did you like the theory?
Report a typo