Saturday 15 August 2015

Java8 Method Reference

Method reference allows you to refer constructors or methods without executing them. You can access a method (or) constructor using :: notation. The only condition is that the methods should be assignable to any Functional Interface.

For example first I am going to explain following application using lambda expression, next same application rewritten using method references.

Following application
a.   Display all employees whose age is > 27.
b.   Display all employees whose firstName starts with ‘S’.
import java.util.Objects;

public class Employee {
  private int id;
  private String firstName;
  private String lastName;
  private int age;
  private String city;
  private double salary;

  public Employee(int id, String firstName, String lastName, int age,
      String city, double salary) {
    super();
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
    this.city = city;
    this.salary = salary;
  }

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public String getCity() {
    return city;
  }

  public void setCity(String city) {
    this.city = city;
  }

  public double getSalary() {
    return salary;
  }

  public void setSalary(double salary) {
    this.salary = salary;
  }

  @Override
  public boolean equals(Object employee) {
    if (Objects.isNull(employee))
      return false;

    if (!(employee instanceof Employee))
      return false;

    Employee emp = (Employee) employee;

    return id == emp.id;
  }

  @Override
  public int hashCode() {
    return Objects.hash(id, firstName, lastName, age);
  }

  @Override
  public String toString() {
    return String.format("%s(%s,%d,%f)", firstName, city, age, salary);
  }

}


import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class EmployeeTest {

  public static <T> List<T> processEmployees(List<T> employees,
      Predicate<T> predicate) {
    List<T> tmpList = new ArrayList<>();
    for (T emp : employees) {
      if (predicate.test(emp)) {
        tmpList.add(emp);
      }
    }
    return tmpList;
  }

  public static void main(String args[]) {
    Employee emp1 = new Employee(1, "Hari Krishna", "Gurram", 26,
        "Bangalore", 40000);
    Employee emp2 = new Employee(2, "Joel", "Chelli", 27, "Hyderabad",
        50000);
    Employee emp3 = new Employee(3, "Shanmukh", "Kummary", 28, "Chennai",
        35000);
    Employee emp4 = new Employee(4, "Harika", "Raghuram", 27, "Chennai",
        76000);
    Employee emp5 = new Employee(5, "Sudheer", "Ganji", 27, "Bangalore",
        90000);
    Employee emp6 = new Employee(6, "Rama Krishna", "Gurram", 27,
        "Bangalore", 56700);
    Employee emp7 = new Employee(7, "PTR", "PTR", 27, "Hyderabad", 123456);
    Employee emp8 = new Employee(8, "Siva krishna", "Ponnam", 28,
        "Hyderabad", 98765);

    List<Employee> employees = new ArrayList<>();

    employees.add(emp1);
    employees.add(emp2);
    employees.add(emp3);
    employees.add(emp4);
    employees.add(emp5);
    employees.add(emp6);
    employees.add(emp7);
    employees.add(emp8);

    List<Employee> ageGreat27;
    List<Employee> firstNameStartsWithS;

    ageGreat27 = processEmployees(employees, (emp) -> emp.getAge() > 27);
    firstNameStartsWithS = processEmployees(employees, (emp) -> emp
        .getFirstName().startsWith("S"));

    System.out.println(ageGreat27);
    System.out.println(firstNameStartsWithS);
  }
}


Output

[Shanmukh(Chennai,28,35000.000000), Siva krishna(Hyderabad,28,98765.000000)]
[Shanmukh(Chennai,28,35000.000000), Sudheer(Bangalore,27,90000.000000), Siva krishna(Hyderabad,28,98765.000000)]


public static <T> List<T> processEmployees(List<T> employees,Predicate<T> predicate) {
         List<T> tmpList = new ArrayList<>();
         for (T emp : employees) {
                  if (predicate.test(emp)) {
                           tmpList.add(emp);
                  }
         }
         return tmpList;
}

Above method takes list of objects, predicate as input and check whether an object matches given predicate or not. If an object satisfies given predicate, it is added to list else not.

ageGreat27 = processEmployees(employees, (emp) -> emp.getAge() > 27);
Above statement list all employees whose age is > 27.

firstNameStartsWithS = processEmployees(employees, (emp) -> emp.getFirstName().startsWith("S"));
Above statement list all employees whose first name starts with S.


Same application can be rewritten using method references like following.

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

public class EmployeeTest {

  public static boolean isAgeGreat27(Employee emp) {
    return emp.getAge() > 27;
  }

  public static boolean isNameStartsWithS(Employee emp) {
    return emp.getFirstName().startsWith("S");
  }

  public static <T> List<T> processEmployees(List<T> employees,
      Predicate<T> predicate) {
    List<T> tmpList = new ArrayList<>();
    for (T emp : employees) {
      if (predicate.test(emp)) {
        tmpList.add(emp);
      }
    }
    return tmpList;
  }

  public static void main(String args[]) {
    Employee emp1 = new Employee(1, "Hari Krishna", "Gurram", 26,
        "Bangalore", 40000);
    Employee emp2 = new Employee(2, "Joel", "Chelli", 27, "Hyderabad",
        50000);
    Employee emp3 = new Employee(3, "Shanmukh", "Kummary", 28, "Chennai",
        35000);
    Employee emp4 = new Employee(4, "Harika", "Raghuram", 27, "Chennai",
        76000);
    Employee emp5 = new Employee(5, "Sudheer", "Ganji", 27, "Bangalore",
        90000);
    Employee emp6 = new Employee(6, "Rama Krishna", "Gurram", 27,
        "Bangalore", 56700);
    Employee emp7 = new Employee(7, "PTR", "PTR", 27, "Hyderabad", 123456);
    Employee emp8 = new Employee(8, "Siva krishna", "Ponnam", 28,
        "Hyderabad", 98765);

    List<Employee> employees = new ArrayList<>();

    employees.add(emp1);
    employees.add(emp2);
    employees.add(emp3);
    employees.add(emp4);
    employees.add(emp5);
    employees.add(emp6);
    employees.add(emp7);
    employees.add(emp8);

    List<Employee> ageGreat27;
    List<Employee> firstNameStartsWithS;

    ageGreat27 = processEmployees(employees, EmployeeTest::isAgeGreat27);
    firstNameStartsWithS = processEmployees(employees,
        EmployeeTest::isNameStartsWithS);

    System.out.println(ageGreat27);
    System.out.println(firstNameStartsWithS);
  }
}


Output

[Shanmukh(Chennai,28,35000.000000), Siva krishna(Hyderabad,28,98765.000000)]
[Shanmukh(Chennai,28,35000.000000), Sudheer(Bangalore,27,90000.000000), Siva krishna(Hyderabad,28,98765.000000)]


1. ageGreat27 = processEmployees(employees, (emp) -> emp.getAge() > 27);

Above statement is equivalent to following statement.

ageGreat27 = processEmployees(employees, EmployeeTest::isAgeGreat27);

In simple terms, method reference is a shorthand form of lambda expressions.

(emp) -> emp.getAge()

Is equivalent to
Employee::getAge

Remember parenthesis are not needed after method name, since we are not calling the method, we are just referring it.

public static boolean isAgeGreat27(Employee emp) {
         return emp.getAge() > 27;
}
        
2. firstNameStartsWithS = processEmployees(employees, (emp) -> emp.getFirstName().startsWith("S"));

Above statement is equivalent to following statement.

firstNameStartsWithS = processEmployees(employees,EmployeeTest::isNameStartsWithS);

public static boolean isNameStartsWithS(Employee emp) {
         return emp.getFirstName().startsWith("S");
}

"EmployeeTest::isNameStartsWithS" statement refers the method isNameStartsWithS of class EmployeeTest.

Kinds of method references
There are four kinds of method references.

Kind Of reference
Example
Reference to a static method
ContainingClass::staticMethodName
Reference to an instance method of a particular object
containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type
ContainingType::methodName
Reference to a constructor
ClassName::new
                 
Previous application is an example of Reference to a static method. 
Reference to an instance method of a particular object
Following example illustrate the use of how to refer an instance method of particular object.

public class EmployeeComparator {
  public int compareByFirstName(Employee emp1, Employee emp2) {
    return emp1.getFirstName().compareTo(emp2.getFirstName());
  }

  public int compareBySalary(Employee emp1, Employee emp2) {
    return (int) (emp1.getSalary() - emp2.getSalary());
  }

}


import java.util.ArrayList;
import java.util.List;

public class SortEmployee {
  public static void main(String args[]) {
    Employee emp1 = new Employee(1, "Hari Krishna", "Gurram", 26,
        "Bangalore", 40000);
    Employee emp2 = new Employee(2, "Joel", "Chelli", 27, "Hyderabad",
        35000);
    Employee emp3 = new Employee(3, "Shanmukh", "Kummary", 28, "Chennai",
        76000);
    Employee emp4 = new Employee(4, "Harika", "Raghuram", 27, "Chennai",
        75000);
    Employee emp5 = new Employee(5, "Sudheer", "Ganji", 27, "Bangalore",
        90000);
    Employee emp6 = new Employee(6, "Rama Krishna", "Gurram", 27,
        "Bangalore", 100000);
    Employee emp7 = new Employee(7, "PTR", "PTR", 27, "Hyderabad", 200000);
    Employee emp8 = new Employee(8, "Siva krishna", "Ponnam", 28,
        "Hyderabad", 55000);

    List<Employee> employees = new ArrayList<>();

    employees.add(emp1);
    employees.add(emp2);
    employees.add(emp3);
    employees.add(emp4);
    employees.add(emp5);
    employees.add(emp6);
    employees.add(emp7);
    employees.add(emp8);

    EmployeeComparator comparisonProvider = new EmployeeComparator();
    System.out.println("Sort employees by first name");
    employees.sort(comparisonProvider::compareByFirstName);
    System.out.println(employees);

    System.out.println("\nSort employees by salary");
    employees.sort(comparisonProvider::compareBySalary);
    System.out.println(employees);

  }
}


Output

Sort employees by first name
[Hari Krishna(Bangalore,26,40000.000000), Harika(Chennai,27,75000.000000), Joel(Hyderabad,27,35000.000000), PTR(Hyderabad,27,200000.000000), Rama Krishna(Bangalore,27,100000.000000), Shanmukh(Chennai,28,76000.000000), Siva krishna(Hyderabad,28,55000.000000), Sudheer(Bangalore,27,90000.000000)]

Sort employees by salary
[Joel(Hyderabad,27,35000.000000), Hari Krishna(Bangalore,26,40000.000000), Siva krishna(Hyderabad,28,55000.000000), Harika(Chennai,27,75000.000000), Shanmukh(Chennai,28,76000.000000), Sudheer(Bangalore,27,90000.000000), Rama Krishna(Bangalore,27,100000.000000), PTR(Hyderabad,27,200000.000000)]


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

Following example illustrate the use of how to refer an arbitrary Object of a Particular Type.

import java.util.Arrays;

public class Test {
  public static void main(String args[]) {
    String[] stringArray = { "Kiran", "Kumar", "Hari", "Krishna",
        "Gopi", "Battu", "Sudheer", "Rama" };
    Arrays.sort(stringArray, String::compareToIgnoreCase);

    for (String s : stringArray) {
      System.out.println(s);
    }
  }
}


Output

Battu
Gopi
Hari
Kiran
Krishna
Kumar
Rama
Sudheer


Reference to a constructor
You can reference a constructor in the same way as a static method by using the name new.

import java.util.*;

public class Test {

  public interface CollectionFactory<T> {
    T get();
  }

  public static <T> T getCollection(CollectionFactory<T> collectionFactory) {
    return collectionFactory.get();
  }

  public static void main(String args[]) {
    Set<String> hashSet1 = getCollection(() -> (new HashSet<String>()));
    Map<String, String> hashmap1 = getCollection(() -> (new HashMap<String, String>()));

    /* Above statements can be written using lambda expressions like below */
    Set<String> hashSet2 = getCollection(HashSet<String>::new);
    Map<String, String> hashmap2 = getCollection(HashMap<String, String>::new);

    System.out.println(hashSet1 instanceof HashSet);
    System.out.println(hashmap1 instanceof HashMap);

    System.out.println(hashSet2 instanceof HashSet);
    System.out.println(hashmap2 instanceof HashMap);
  }
}


Output

true
true
true
true


When you cannot use Method reference?
You cannot pass arguments to the method reference.

When I can use method reference?
When a Lambda expression is invoking already defined method, you can replace it with reference to that method.






Prevoius                                                 Next                                                 Home

No comments:

Post a Comment