Method References

A method reference is a syntax for defining a reference to a single method.

Method references can be used in place of a class implementing an interface, if the interface has only one abstract method (called a functional interface).

The syntax for a method reference is:

Syntax What it refers to Example
Classname::methodName a static method Math::sqrt
objectref::methodName an instance method of an object this::toString
Classname::new a constructor Person::new

Motivation

Many Java interfaces have only one method:

Interface Method with Signature What Uses It?
Runnable void run( ) Thread.start(Runnable)
Comparator<T> int compare(T a, T b) Collections.sort(List,Comparator)
EventHandler<E> void handle(E event) Button.setOnAction(EventHandler)
Consumer<T> void accept(T arg) collection.forEach(Consumer)

In older versions of Java, the only way to use interfaces was to write a class that implements the interface.

It would be nice if we could just pass a method to the code that requires the interface, for example:

// add event handler to a button
Button button = new Button("Press me");
button.setOnAction( handlePress );

public void handlePress(ActionEvent event) {...}

The button would simply call the handlePress method – without the complexity of writing a class just so we can define one method!

Java Method References lets us do something like this:

Button button = new Button("Press me");
button.setOnAction( this::handlePress );

Any place that requires an interface, where the interface has only one abstract method (such as Runnable, EventHandler, Comparator) you can pass a method reference instead!

The only requirement is that the Method Reference should have a signature that is the same as or compatible with the method in the interface.

For example, Consumer&lt;String&gt; has a method void accept(String s). Instead of writing a class for a Consumer, we can use any of these methods:

void save(String s) { writer.write(s); }  // writer is a Writer object

void print(Object o) { System.out.println(o); }

void showDialog(String msg) { 
     Alert alert = new Alert(Alert.AlertType.INFORMATION);
     alert.setHeaderText( msg );
     alert.showAndWait();
}

Even though the method names are not the same as in the interface (accept), the method signature matches, so they can be used as method reference in place of a Consumer.

The print(Object o) method works because it accepts any kind of Object, including String.

List words = Arrays.asList("method","reference","is","easy"};
// Use a method reference as Consumer:
list.forEach( this::save );
list.forEach( this::print );
list.forEach( this::showDialog );

Example: Method Reference for Event Handlers

It is often convenient to write event handler methods for JavaFX applications as methods in the Controller class for a JavaFX scene.
This is convenient because the controller has a reference to the components in the scene, and the event handler usually needs to refer to those components.

public class LoginController {
    @FXML
    TextField loginField;
    @FXML
    Button loginButton;

    /** Handler for login event. */
    public void handleLogin(ActionEvent event) {
        String username = loginField.getText();
        String password = passwdField.getText();
        if ( authenticate(username,password) ) ...
    }

    loginButton.setOnAction( this::handleLogin );

The method handleLogin looks like an event handler – it has the correct parameter and returns void, but it is not part of a class that “implements EventHander”.

A method reference defines a reference to a method. You can use it any place that expects a reference to something implementing the interface. For example, we can write:

    EventHandler<ActionEvent> handler = this::handleLogin;
    loginButton.setOnAction( handler );

since it pretty clear what the intention is, you can just use the method reference directly to set the event handler:

    loginButton.setOnAction( this::handleLogin );

The setOnAction method accepts a method reference this::handleLogin because the method signature matches the method signature in the EventHandler<ActionEvent> interface.

Weird Use of Method Reference

void System.out.println(Object) has a compatible signature with EventHandler.handle(Event), because System.out.println can accept any Event object. So we could print all the events on System.out by writing:

    loginButton.setOnAction( System.out::println );

Example: Write a Consumer

java.util.function.Consumer is an interface with one method:

// T is a type parameter of Consumer
public void accept(T obj);

Consumer is used by many Streams methods when they want a method to “consume” an object. Every Collection and Iterable has a forEach(Consumer) method which works like this:

collection<T>.forEach( Consumer<T> consumer )

This is a short-cut for a “for-each” loop over elements in the Collection!
Its the same as writing:

for(T item: collection) {
    consumer.accept( item );
}

forEach Example: print elements of a List

The old way of printing element in a list is using a loop:

List<String> fruit = List.of("Apple", "Orange", "Grape",...);
for(String s: fruit) {
    System.out.println( s );
}

Let’s change that to a fruit.forEach() statement.

Consumer has one method that accepts a type (T). The signature is:

void accept(T s);

Since fruit is a list of String, this will be void accept(String s).

Notice that System.out.println (which is overloaded) also has this signature:

// In System.out:
void println(String s);

So we can use a method reference to refer to System.out.println as a Consumer:

Consumer<String> print = System.out::println;
fruit.forEach( print );

The method reference is short and clear, so let’s simplify without lose of clarity:

fruit.forEach( System.out::println );

When To Use a Method Reference

Whenever you see a Lambda or Anonymous Class that just calls another method and passes its own parameter to that method, you can usually rewrite it as a method reference:

// compare strings ignoring case of letters
Comparator<String> comp = (a,b) -> a.compareToIgnoreCase(b);

is same as:

// compare strings ignoring case of letters
Comparator<String> comp =  String::compareToIgnoreCase;

Because compareToIgnoreCase is an instance method, Java uses the first parameter (a) as the “this” object when it invokes compareToIgnoreCase.