Lambda and Functional Interfaces

Understanding Method & Constructor reference in JAVA

April 8, 2020

Before start learning this new concept of Method Reference and Constructor Reference in JAVA let us first revise by listing down what we have studied till now through our previous blogs on functional programming.

  • In our first three blogs we discussed what is the need of functional programming and why is is the demand of the hour. In the next couple of blogs we observed that to enable Java for taking benefits from functional programming Lambda was introduced and we can execute Lambda with help of Functional Interfaces. We also shared with you the practice blog for writing Lambda & Functional Interfaces for various method signatures.
  • Then we discussed the difference between Imperative, Declarative & Functional programming and their significance.
  • Another important concept which we shared with you was predefined functional interfaces. The blog discussed various use cases and problem statements explaining the implementation of the predefined interfaces in JAVA

Now from our previous blogs we already know that with a Functional Interface we can pass the behaviour on the Fly. The question that comes to our mind here is Can we also refer to the already implemented method rather than writing the same behaviour or implementation again.

This means that if we writing a Lambda or an Algorihutm, can we match an already existing method definition or behaviour with the no. of type of Parameters. Can we use this matching implementation at the place of Lambda?

The answer is Yes. This is possible and its called Method Reference. (Since we are directly referring to an existing method)
We also have Constructor Reference for object creation to refer to a constructor.
And this is what we are going to discuss in this blog.

Method Reference

Method references can also be used to call a method just like a Lambda. Particularly where there are preexisting code that we would like to reuse we can simplify the implementation of the functional interface by using method references.

Method refrence uses :: operator to refer the existing implementation.

Consider the ConsumerPractice Class which we discussed in previous blog. When we create a Consumer

Consumer<Integer> consumer = e ->System.out.println(e);

A Consumer takes something but returns nothing, It takes the element and just print the element on the console and returns nothing. Similarly the println method also takes something and returns nothing so it is doing the same functionality as the Consumer. Thus we already have the behavior we want, so we can avoid creating it again. So rather than writing a Lambda for println, we can directly refer to this method.

For this :: operator is used to separate the class or object from the method name.

So it becomes

Consumer<Integer> consumer = System.out :: println;

There is no need to write the arguments. Thus we have seen that how a Method Reference works.

There are 4 types of Method References

Let us discuss each of these in detail.

a) Method reference to static method :

Here first comes the class name then :: operator and then the static method name we are referring to.

ClassName::MethodName

ex : Integer::parseInt, Math::max

b) Method reference to instance method of an existing object :

ReferenceVariable::MethodName

We use this syntax when we are referring to an instance method of already existing object.

ex : s::getName

where ‘s’ is a reference variable referring to Student object which already exist.

c) Method reference to instance method of non-existing object :

ClassName::MethodName

We use this syntax when we are referring to an instance method by passing reference variables as an argument.

ex : (Student s) -> s.getName() can be written as Student::getName

d) Constructor References
We can also refer to the constructor of a class same as method references. Syntax for referring to constructor is,

ClassName::new

Ex : Student::new

Now Let us take a closer look on method reference and see how to practically use :: Operator

package com.basicsstrong.functionalprogramming.section5;

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

public class MethodReferenceDemo {	
	public static void main(String[] args) {

		Employee employee1 = new Employee("John",1001,11000);
		Employee employee2 = new Employee("Erick",1001,11000);
		Employee employee3 = new Employee("Michele",1001,9000);
				
		ArrayList<Employee> employees = new ArrayList<>(); 
		       employees.add(employee1);employees.add(employee2);employees.add(employee3);
		
		for(Employee e : employees) {
			System.out.println(e.getEmpId());
		}
		
		List<Employee> list = filterEmployees(e -> e.getSalary()>10000, employees);
		System.out.println(list.toString());
		List<Employee> list1 = filterEmployees(MethodReferenceDemo::highSalary, employees);
		System.out.println(list.toString());
		Function<Employee, String> fun = e -> e.getName();
		System.out.println(fun.apply(employee1));
		
		Function<Employee,String> fun1 = Employee::getName;
		System.out.println(fun1.apply(employee1));
		MethodReferenceDemo demo = new MethodReferenceDemo();
		Function<MethodReferenceDemo, Integer> salary = obj -> obj.getSalary(employee1) ;
		System.out.println(salary.apply(demo));
		Function<Employee, Integer> salary1 = demo::getSalary;
	}
	
	public static List filterEmployees(Predicate<Employee> p, ArrayList<Employee> l){
		
	ArrayList<Employee> newList= new ArrayList<>();
	
	for(Employee e : l) {
	
		if(p.test(e)) {
			newList.add(e);
		}
	}
	
	return newList;
	
	}
	
	public static boolean highSalary(Employee emp){
		
		if(emp.getSalary()>1000) {
			return true;
		}else {
			return false;
		}
	}
	
	public int getSalary(Employee emp) {
		
		return emp.getSalary();
	}
	
}

Constructor Reference

Let us consider the following code where we need to execute some tasks in parallel so we will need threads to execute theses tasks in parallel. Lets create threads

public class ConstructorReference {
	
	public static void main(String[] args) {
            Function<Runnable, Thread> threadGenerator = Thread :: new;
            Runnable task1 = () -> System.out.println("Task 1 executed"); 		
		
            Runnable task2 = () -> System.out.println("Task 2 executed"); 	

           Thread thread1 = threadGenerator.apply(task1);	
           Thread thread2 = threadGenerator.apply(task2);

          thread1.start();
          thread2.start();
}
}	

Executing this code displays

Task 1 executed

Task 2 executed