Java 8

Java8 – Method References

One of the most welcome changes in Java 8 was the introduction of lambda expressions, as these allow us to forego anonymous classes, greatly reducing boilerplate code and improving readability.

Method references are a special type of lambda expressions. They’re often used to create simple lambda expressions by referencing existing methods.

There are four kinds of method references:

  1. Static methods
  2. Instance methods of particular objects
  3. Instance methods of an arbitrary object of a particular type
  4. Constructor

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

1. Reference to a Static Method

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

List<String> wordList = Arrays.asList("hello", "hitechpoints", "readers!");

We can achieve this by leveraging a simple lambda expression calling the StringUtils.upperCase() method directly:

wordList.forEach(word -> StringUtils.upperCase(word));

Or, we can use a method reference to simply refer to the upperCase static method:

messages.forEach(StringUtils::upperCase);

Notice that method references always utilize the :: operator.

2. Reference to an Instance Method of a Particular Object

To demonstrate this type of method reference, let’s consider two classes

public class Car {
  private String brand;
  private int price;

  public Car(String brand, Integer price) {
	  this.brand = brand;
	  this.price = price;
  }  

  public String getBrand() {
		return brand;
  }
  public void setBrand(String brand) {
		this.brand = brand;
  }
  public int getPrice() {
	 return price;
  }
  public void setPrice(int price) {
	 this.price = price;
  } 
}
import java.util.Comparator;

public class CarComparator implements Comparator<Car> {
	@Override
	public int compare(Car c1, Car c2) {
		return c1.getPrice().compareTo(c2.getPrice());
	}
}

And, let’s create a CarComparator object to compare car price:

CarComparator carPriceComparator = new CarComparator();

We could use a lambda expression to sort car by price, but we’d need to specify two cars for comparison:

List<Car> carList = new ArrayList<Car>();
carList.add(new Car("BMW X1", 52000000));
carList.add(new Car("BMW X7", 152000000));
		
carList.stream().sorted((c1, c2) -> carPriceComparator.compare(c1, c2));

Instead, we can use a method reference to have the compiler handle parameter passing for us

carList.stream().sorted(carPriceComparator::compare);

The method reference is much cleaner and more readable, as our intention is clearly shown by the code.

3. Reference to an Instance Method of an Arbitrary Object of a Particular Type

This type of method reference is similar to the previous example, but without having to create a custom object to perform the comparison.

Let’s create an Integer list that we want to sort

List<Integer> list = Arrays.asList(5, 3, 50, 24, 40, 2, 9, 18);

If we use a classic lambda expression, both parameters need to be explicitly passed, while using a method reference is much more straightforward

numbers.stream().sorted((a, b) -> a.compareTo(b));

numbers.stream().sorted(Integer::compareTo);

Even though it’s still a one-liner, the method reference is much easier to read and understand.

4. Reference to a Constructor

We can reference a constructor in the same way that we referenced a static method in our first example. The only difference is that we’ll use the new keyword.

Let’s create a Car array out of a String list with different brands

List<String> list = Arrays.asList("BMW X1", "Audi", "Honda");

Then, we’ll add a new constructor to our Car class

public Car(String brand) {
   this.brand = brand;
   this.price = 0;
}

Next, we’ll use our new constructor from a method reference and make a Car array from the original String list

list.stream()
.map(Car::new)
.toArray(Car[]::new);

Notice how we called both Car and Array constructors using a method reference, giving our code a much more concise and clear appearance.

About the Author: Elavarasan PK

Technical Specialist, Intersoft Data Labs