1. The introduction

One of the most popular development changes in Java8 is the introduction of lambda expressions, which allow us to abandon anonymous classes, greatly reducing boilerplate code and improving readability. A method reference is a special type of lambda expression. They typically create simple lambda expressions by referring to existing methods.

There are four types of method references:

  • A static method
  • Instance method of a particular object
  • Instance methods of arbitrary objects of a particular type
  • A constructor

In this article, we’ll explore method references in Java.

2. Reference static methods

We’ll begin with a very simple example, capitalizing and printing a list of Strings:

Let’s start with a very simple example where the string is converted to uppercase and printed:

List<String> messages = Arrays.asList("hello"."baeldung"."readers!");
Copy the code

We can call the Stringutils.capitalize () method directly through a simple lambda expression:

messages.forEach(word -> StringUtils.capitalize(word));
Copy the code

Alternatively, we can use a method reference to simply reference the capitalize static method:

messages.forEach(StringUtils::capitalize);
Copy the code

Note that method references should use the :: operator.

3. Reference instance methods of specific objects

To demonstrate this type of method reference, we create the following two classes:

public class Bicycle {
 
    private String brand;
    private Integer frameSize;
    // standard constructor, getters and setters
}
 
public class BicycleComparator implements Comparator {
 
    @Override
    public int compare(Bicycle a, Bicycle b) {
        returna.getFrameSize().compareTo(b.getFrameSize()); }}Copy the code

Create a BicycleComparator object to compare bicycle sizes:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();
Copy the code

We can use lambda expressions to sort the bikes by size, but we need to specify two bike instances for comparison:

createBicyclesList().stream()
  .sorted((a, b) -> bikeFrameSizeComparator.compare(a, b));
Copy the code

We can use method references to get the compiler to pass the handle argument to us:

createBicyclesList().stream()
  .sorted(bikeFrameSizeComparator::compare);
Copy the code

4. Instance methods that reference arbitrary objects of a particular type

This type of method reference is similar to the previous example, but you do not have to create a custom object to perform the comparison.

Let’s create a list of Integer integers to sort:

List<Integer> numbers = Arrays.asList(5.3.50.24.40.2.9.18);
Copy the code

If we use classic lambda expressions, both arguments need to be passed explicitly, while using method references is much simpler:

numbers.stream()
  .sorted((a, b) -> a.compareTo(b));
numbers.stream()
  .sorted(Integer::compareTo);
Copy the code

Although it is still a single line of code, the method reference is easier to read and understand.

5. Reference constructors

We can refer to the constructor just as we did to the static method in the first example. The only difference is that the new keyword is required. Now we create an array of bicycles from the String list of different brands:

List<String> bikeBrands = Arrays.asList("Giant"."Scott"."Trek"."GT");
Copy the code

First, we’ll add a new constructor to the Bicycle class:

public Bicycle(String brand) {
    this.brand = brand;
    this.frameSize = 0;
}
Copy the code

Next, we’ll use the new constructor in the method reference and generate an array of Bicycle from the original String list:

bikeBrands.stream()
  .map(Bicycle::new)
  .toArray(Bicycle[]::new);
Copy the code

Notice how the Bicycle and Array constructors are called using method references to make the code look cleaner.

6. Other examples and limitations

So far, method references are a great way to make your code very clear and readable. However, we can’t use them as a substitute for various lambda expressions because they have some limitations.

Their main limitation is due to their greatest advantage: the output of the previous expression needs to match the input parameters of the referenced method declaration.

Consider an example of this restriction:

createBicyclesList().forEach(b -> System.out.printf(
  "Bike brand is '%s' and frame size is '%d'%n",
  b.getBrand(),
  b.getFrameSize()));
Copy the code

This simple example cannot be represented by a method reference, because in our example the printf method requires three arguments, whereas using *createBicyclesList().foreach ()* allows the method to reference only one argument (Bicycle object).

Finally, let’s examine how to create a no-operation function that can be referenced from a lambda expression.

In this case, we want to use a lambda expression without its arguments.

First, create the doNothingAtAll method:

private static <T> void doNothingAtAll(Object... o) {}Copy the code

Because this is a varargs method, it can be executed in any lambda expression, regardless of the number of referenced objects or arguments. Let’s see what it does:

createBicyclesList()
  .forEach((o) -> MethodReferenceExamples.doNothingAtAll(o));
Copy the code

7. To summarize

In this article, we learned about method references in Java and how to use them to replace lambda expressions, improving readability and clarifying programming intentions.