1. Function methods
If an interface has only one method, a variable of that interface type can be assigned a value given by a lambda expression (lambda function). Such interfaces became known as functional interfaces (after Java added support for lambda functions).
For example, Java has the Consumer<Type>
interface, which has the accept(Type obj)
method. Why is this interface needed?
In Java 8, collections have a forEach()
method, which lets you perform some action for each element of the collection. And here the Consumer<T>
functional interface is used to pass the action to the forEach()
method.
Here's how you can display all elements of a collection:
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");
list.forEach( (s) -> System.out.println(s) );
The compiler will convert the code above to the code below:
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");
list.forEach(new Consumer<String>()
{
public void accept(String s)
{
System.out.println(s));
}
});
The first version is definitely shorter than the second. And while code with lambda expressions is hard to read, code with anonymous inner classes is sometimes even harder to read.
2. Method reference
However, our lambda expression code can be written even shorter.
First, you can omit the parentheses around the s
parameter:
list.forEach( (s) -> System.out.println(s) );
list.forEach( s -> System.out.println(s) );
This can only be done only if there is one parameter. If there are multiple parameters, then you must use parentheses.
And second, you can write it like this:
list.forEach( System.out::println );
This is the exact same notation. Note that there are no parentheses after the println
.
Here we have the same code — a method call:
object::method
x -> object.method(x)
Think about it: we wanted to perform some action for each element of the list
collection. If the action is a single function call (such as println()
), then it make senses to simply pass the function to the method as a parameter.
But how do we explain to the compiler that we want to pass the method rather than call it? To do this, instead of the dot operator, we use two colons before the method name. A single colon is already used to indicate the ternary operator.
This is the simplest and most compact notation.
3. Constructor
Method references with double colons are very handy when we work with I/O streams. You will see this a little later.
In the meantime, let's talk about 3 popular ways to pass a method reference:
Reference to a method of an object
To pass a reference to a method of an object, you need to write something like object::method
.
This code is equivalent to x -> object.method(x)
.
The special this
and super
variables can be used as the object.
Reference to a method of a class
To pass a reference to a static method, you need to write something like class::method
. This code gets converted to code like x -> class.method(x);
Reference to a constructor
A constructor behaves similarly to a static class method, so you can also pass a reference to a constructor. This is how it looks: class::new
.
For example, you can get around type erasure for collections and pass the toArray()
method a reference to a constructor that will create the desired array: toArray(int[]::new);
GO TO FULL VERSION