Method References In Java

Method references are special representation of lambda expressions. In fact they are mostly used to represent lambda expressions in a more readable, compact format.

For example, the Lambda, (Class obj) -> obj.method() can be written in a more compact manner as Class :: method. We have eliminated the brackets and -> and don’t you think it looks more readable ?

They are used to reference methods by name, and letting compiler automatically, infer, the parameters to be passed.

Representation

Method references are denoted by “::” symbol. In a way, they are meant to make lambda expressions more compact and easier to represent. A lambda expression of form (Class obj) -> obj.method() is equivalent to the method reference Class::method and this makes the code more compact. There are mostly three types of method references

  • Static Method References
  • Instance Method References
  • Constructor References

Static Method References

They follow the syntax – classname :: method . Parameters to the method, are detected by the compiler automatically; no need to explicitly pass the parameter to the function.

package com.stackrules.java;
 
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
 
/**
 * Demonstrates the usage of method reference
 */
public class StaticMethodReference {
 
    public static void main(String args[]) {
        List<String> fruits = new ArrayList<String>();
 
        fruits.add("Apple");
        fruits.add("Mango");
        fruits.add("Banana");
        fruits.add("Grapes");
        fruits.add("Oranges");
 
        // Here we refere the println method using the :: operator
        // The fruits is not passed as argument to println explicitly like println(fruits),
        // it is inferred by compiler automatically
        fruits.forEach(System.out::println);
 
        List<String> upperCaseFruits1 = fruits.stream().map(f->f.toUpperCase()).collect(Collectors.toList());
        List<String> upperCaseFruits2 = fruits.stream().map(String::toUpperCase).collect(Collectors.toList());
 
 
    }
}

In the above code, we were able to replace the lambda expression for converting fruits to upperCase with String::toUpperCase.

Instance Method Reference

In this category, lambda expressions of the form arg -> someObj.method(arg), are replaced with obj::method

// A traditional way of adding items to List
 List<String> fruits = new ArrayList<String>();
 fruits.add("Apple");
 fruits.add("Mango");
 fruits.add("Banana");
 fruits.add("Grapes");
 fruits.add("Oranges");

Above code, creates an array and adds items to it. This can be written in a better way as below using streams and the instance method reference. The multi line code has been replaced with a single line code ! . Yes, nobody likes lengthy code 🙂

 Stream.of("Apple","Mango","Orange").forEach(fruits::add);

Instance Method – Multiple Parameters

Method reference on an instance can be used even when the method takes more than one parameter.

package com.stackrules.java;
 
 
import java.util.function.BiConsumer;
 
public class MethodReferenceMultiArg {
 
    public static  void main(String[] args) {
 
        Shape blueCirlce = new Shape();
        blueCirlce.setParam("blue","circle");
        BiConsumer<String, String> setNameMethRef= blueCirlce::setParam;
    }
}
 
class Shape {
 
    private String color;
    private String type;
 
    public void setParam(String color,String type) {
        this.color = color;
        this.type  = type;
    }
 
}

Constructor References

Now let us look at how constructor calls can be represented using method references. A lambda expression of the form, () -> new SomeClass() can be represented using constructor reference as SomeClass ::new

Let us create an array that is used to pass the name of fruits to a class. Our aim is to create an Array of Fruits initialized with its name.

List<String> fruitNames = Arrays.asList("Apple", "Orange", "Guava", "Grapes");

Now let us create the Fruit class

class Fruit {
     
    private String name;
    Fruit(String name) {
        this.name = name;
    }
     
}

Let us now create a List of Fruits that gets its name initialized from the fruitNames list shown above.

fruitNames.stream().map(Fruit::new).toArray(Fruit[]::new);

Leave a Comment