Sunday, September 18, 2022

Method Reference in Java

We generally use lambda expressions to create anonymous methods but sometimes lambda expressions are also used to call an existing method. There is another feature provided in Java 8 related to lambda expression called method reference in Java that provides a clearer alternative to refer to the existing method by name.

Important thing to note about Java method references is that they provide a way to refer to a method, they don't execute the method.

Method reference in Java relates to lambda expression as they also require a target type context and at the time of execution they also create an instance of functional interface.

How lambda expressions and method references differ is where lambda expressions let us define anonymous methods which can be used as an instance of functional interfaces. Method references do the same thing, but with existing methods.


Kinds of Method References in Java

There are four kinds of method references in Java.

Kind Example Syntax
Reference to a static method ContainingClass::staticMethodName ClassName::methodName
Reference to an instance method of a particular object containingObject::instanceMethodName objRef::methodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName ClassName::instanceMethodName
Reference to a constructor ClassName::new classname::new

Notice that the class name is separated from the method name by a double colon. The :: is a new separator (known as double colon operator) that has been added in Java 8.

Method References to static methods

The following example code shows a static method reference in Java. In the example there is a functional interface IMyStringFunc with a method stringFunc.

import java.util.Arrays;

@FunctionalInterface
interface IMyStringFunc<T, R>{
  R stringFunc(T t);
}
public class MethodRefDemo {
  public static void main(String[] args) {
    //String array of names
    String[] strNames = new String[]{"Ram", "shyam", "Ramesh", "John", "brad", 
           "Suresh"};
    // method reference to the static method sortName
    IMyStringFunc<String[],String[]> stringFunc = SortClass::sortName;
    // Here calling strngFunc will refer to the implementing method
    // sortName()
    String[] sortedNames = stringFunc.stringFunc(strNames);
    for(String name : sortedNames){
      System.out.println(name);
    }
  }
}

class SortClass{
  // A static method that sorts an array
  static String[] sortName(String[] names) {
    //Sorting array using sort method (case sensitive)
    Arrays.sort(names);
    return names;
  }
}

Output

John
Ram
Ramesh
Suresh
brad
shyam

In the code in this line SortClass::sortName; existing method sortName() is referred using method reference. This works because sortName is compatible with the IMyStringFunc functional interface. Thus, the expression SortClass::sortName evaluates to a reference to an object in which method sortName provides the implementation of abstract method stringFunc in functional interface IMyStringFunc.

Method References to Instance Methods

To pass a reference to an instance method of a particular object is similar to static method reference, instead of class we need to use object

objRef::methodName

Let's see the same example again with object

import java.util.Arrays;
@FunctionalInterface
interface IMyStringFunc<T, R>{
  R stringFunc(T t);
}

public class MethodRefDemo {
  public static void main(String[] args) {
    // creating an object
    SortClass sc = new SortClass();
    //String array of names
    String[] strNames = new String[]{"Ram", "shyam", "Ramesh", "John", "brad", 
            "Suresh"};  
    // method reference to the instance method sortName
    IMyStringFunc<String[],String[]> refObj = sc::sortName;
    String[] sortedNames = refObj.stringFunc(strNames);
    for(String name : sortedNames){
      System.out.println(name);
    }
  }
}

class SortClass{
  // A non-static method that sorts an array
  String[] sortName(String[] names) {
    //Sorting array using sort method (case sensitive)
    Arrays.sort(names);
    return names;
  }
}

Reference to an instance method of an arbitrary object of a particular type

There may be a situation when you want to specify an instance method that can be used with any object of a given class without any particular object. In that case name of the class is used even when a non-static method is specified.

ClassName::instanceMethodName

Let's take one example where we have a Person class and we have a list of Person object. We want to call getFirstName() method on all those person objects.

Person Class

public class Person {
  private String firstName;
  private String lastName;
  private int age;
  private char gender;
  public Person(String firstName, String lastName, int age, char gender){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.gender = gender;
  }
    
  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public int getAge() {
    return age;
  }
  public char getGender() {
    return gender;
  }
    
  public String toString(){
    StringBuffer sb = new StringBuffer();
    sb.append(getFirstName()).append(" ");
    sb.append(getLastName()).append(" ");
    sb.append(getAge()).append(" ");
    sb.append(getGender());
    return sb.toString();      
  }
}
@FunctionalInterface
interface IMyStringFunc<T, R>{
  R stringFunc(T t);
}

public class MethodRefDemo {
  public static void main(String[] args) {
    List<Person> personList = createList();
    List allNames = MethodRefDemo.listAllNames (personList, Person::getFirstName);
    System.out.println("" + allNames);
  }
    
  //Utitlity method to create list
  private static List<Person> createList(){
    List<Person> tempList = new ArrayList<Person>();
    IMyFunc createObj = Person::new;
    Person person = createObj.getRef("Ram","Tiwari", 50, 'M');
    tempList.add(person);
    person = createObj.getRef("Prem", "Chopra", 13, 'M');
    tempList.add(person);
    person = createObj.getRef("Tanuja", "Trivedi", 30, 'F');
    tempList.add(person);
    person = createObj.getRef("Manoj", "Sharma", 40, 'M');
    tempList.add(person);
    person = createObj.getRef("John", "Trevor", 70, 'M');
    tempList.add(person);
    person = createObj.getRef("Alicia", "Sliver", 17, 'F');
    tempList.add(person);
    System.out.println("List elements are - ");
    System.out.println(tempList);
    return tempList;
  }

  private static List<String> listAllNames(List<Person> person, 
         IMyStringFunc<Person, String> func){
    List<String> result = new ArrayList<String>();
    person.forEach(x -> result.add(func.stringFunc(x)));
    return result;
  }
}

Notice this line Person::getFirstName here we are calling getFirstName method on all the objects of the list not any one particular object.

Reference to a Constructor

A constructor can be referenced in the same way as a static method by using new.

public class Person {
  private String firstName;
  private String lastName;
  private int age;
  private char gender;
  public Person(String firstName, String lastName, int age, char gender){
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.gender = gender;
  }
    
  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public int getAge() {
    return age;
  }
  public char getGender() {
    return gender;
  }
    
  public String toString(){
    StringBuffer sb = new StringBuffer();
    sb.append(getFirstName()).append(" ");
    sb.append(getLastName()).append(" ");
    sb.append(getAge()).append(" ");
    sb.append(getGender());
    return sb.toString();
  }
}
@FunctionalInterface
interface IMyFunc {
  Person getRef(String firstName, String lastName, int age, char gender);
}

public class LambdaDemo {
  public static void main(String[] args) {
    List<Person> personList = createList();
    System.out.println("Name - " + personList.get(0).getFirstName());
  }
  // Utitlity method to create list
  private static List<Person> createList(){
    List<Person> tempList = new ArrayList<Person>();
    IMyFunc createObj = Person::new;
    Person person = createObj.getRef("Ram","Tiwari", 50, 'M');
    tempList.add(person);
    person = createObj.getRef("Prem", "Chopra", 13, 'M');
    tempList.add(person);
    person = createObj.getRef("Tanuja", "Trivedi", 30, 'F');
    tempList.add(person);
    person = createObj.getRef("Manoj", "Sharma", 40, 'M');
    tempList.add(person);
    person = createObj.getRef("John", "Trevor", 70, 'M');
    tempList.add(person);
    person = createObj.getRef("Alicia", "Sliver", 17, 'F');
    tempList.add(person);
    System.out.println("List elements are - ");
    System.out.println(tempList);
    return tempList;
  }
}

Output

List elements are - 
[Ram Tiwari 50 M, Prem Chopra 13 M, Tanuja Trivedi 30 F, Manoj Sharma 40 M, John Trevor 70 M, Alicia Sliver 17 F]
Name - Ram

In the program notice that the getRef method of the IMyFunc returns a reference of type Person and has String, String, int and char as parameters. Also notice that in the Person class there is a constructor which specifies String, String, int and char as parameters.

Using this line Person::new a constructor reference to a Person constructor is created. Functional interface method also takes the same params thus the constructor of the Person class is referred through it.

That's all for this topic Method Reference in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Functional Interface Annotation in Java
  2. How to Resolve Local Variable Defined in an Enclosing Scope Must be Final or Effectively Final Error
  3. Java Lambda Expression as Method Parameter
  4. Java Lambda Expression And Variable Scope
  5. Java Lambda Expressions Interview Questions And Answers

You may also like-

  1. How HashSet Works Internally in Java
  2. equals() And hashCode() Methods in Java
  3. final Vs finally Vs finalize in Java
  4. Difference Between throw And throws in Java
  5. static Method Overloading or Overriding in Java
  6. Why wait(), notify() And notifyAll() Methods Are in Object Class And Not in Thread Class
  7. PermGen Space Removal in Java 8
  8. How to Create PDF From XML Using Apache FOP