Recent Tutorials and Articles
    Working with Lambda Expressions in Java
    Published on: 11th August 2016

    In this tutorial, we will be working with Lambda expressions and method references introduced in Java 8 and understand their usages.

    Abstract


    Java 8 has introduced quite a few features to support Functional Programming. One important aspect of Functional Programming is that functions are first class citizens and can be passed around just like data. Lambda expressions have been introduced in Java 8 to make it possible to pass logic as data.

    We howerver need to note that Java already provided us with anonymous classes that allow us to pass logic as data. Let's consider following example of providing the comparison logic to TreeSet -

    final SortedSet<Employee> employees = new TreeSet<>(
    		new Comparator<Employee>() {
    
    			@Override
    			public int compare(Employee o1, Employee o2) {
    				return o1.getId().compareTo(o2.getId());
    			}
    		}
    );

    In this case, we have created an anonymous class implementing Comparator interface. Then we are passing this class, containing comparison logic, to TreeSet class that will utilize it for sorting the inserted Employee objects.

    Although we are able to pass logic using anonymous classes, its syntax is quite verbose. In the above example, there is just one below line containing logic but we are passing a whole class.

    o1.getId().compareTo(o2.getId());

    It would be nice if there was some way to just pass the logic instead of whole class. This is where Lambda expressions come into picture!

     

    Lambda Expressions


    Lambdra expressions are used to represent the computation logic/behaviour i.e. functions. Before going into more details, let's see how our above example will look like using Lambda expressions

    final SortedSet<Employee> employees = new TreeSet<Employee>(
    		(emp1,emp2) -> emp1.getId().compareTo(emp2.getId()) //Lambda Expression
    	);

    As we can see this is much more concise in comparison to anonymous class. Here are following points worth noting in above Lambda expression -

    • Input arguments (emp1, emp2) : Lambda expression starts with input arguments in paraenthesis. Type of input parameters is inferred from target type. In this case, we are expecting a Comparator interface implementation. Since Comparator only has one abstract method compare(T, T), our Lambda expression input arguments will be of Employee type. 
    • Lambda operator (->) : This operator separates Lamba input arguments from Lambda expressions body.
    • Lambda body : This represents the function logic to be passed as method argument. Last line of Lambda expression body represents a return statement if function inteface method is expecting a value to be returned. In our example, since there is just one line in Lambda body and compare method expects a return value, output of our Lambda expression, that is integer, is returned to caller.

    Note: Lambda expressions can only be used in place of Functional interfaces i.e. interfaces with just one abstract type. In case of more details on this, please read about Interfaces in Java 8.

    Since Lambda expressions replace functional interfaces, Lambda expression type is same as corresponding functional interface. In our case, type of our lambda expression is Comparator and hence it is perfectly valid to use following statement - 

    final Comparator<Employee> empComparator = (emp1,emp2) -> emp1.getId().compareTo(emp2.getId());

     

    Let's now see different types of Lambda expressions - 

    Lambda expressions with no input parameters

    Lambda expressions need not have input parameters and can be used for expressing logic without input parameters. For example, below code submits a Callable to Single Thread Executor by returning Future object (String 'Execution done!" will be returned to caller after thread has finished executing) -

    final Future<String> future = Executors.newSingleThreadExecutor().submit(
    			() -> "Execution done!"
    		);

     

    Lambda expressions with no return type

    Similarly, Lambda expressions also work for logic that does not return anything. In this case, last statement do not return any value. Here is code with Lambda expression for submitting a Runnable to Single Thread Executor. Code is same as Callable with only difference that it does not return any value.

    Executors.newSingleThreadExecutor().submit(
    			() -> System.out.println("Execution done!")
    		);

     

    Lambda expression with multiple lines body

    Lambda expressions need not be finished in one line and can span over multiple lines. We can wrap Lambda expression body in braces {} in case of multi line expressions as follows - 

    final Future<String> future = Executors.newSingleThreadExecutor().submit(
    			() -> {
    				Thread.sleep(1000);
    				return "Execution Done after waiting for 1 second!";
    			}
    		);

     

    Method References


    Lambda expressions do a good job in making code compact but nothing really beats method references. Just like Lambda expressions, ,method references can only be applied in place of functional interfaces. Method references are of three types - Static method references, constructor references and instance methods references.

    Static Method References

    Let's consider our earlier example of a SortedSet of Employee objects. Earlier we had written logic inside a Lambda expression but imagine you already have a class called EmployeeUtils containing comparison logic as follows - 

    public class EmployeeUtils{
    	
    	public static int compareEmployees(Employee o1, Employee o2) {
    		if(o1 == o2) {
    			return 0;
    		}
    		final Long idDiff = o1.getId() - o2.getId();
    		if(idDiff == 0) {
    			return o1.getName().compareTo(o2.getName());
    		}
    		return idDiff.intValue();
    	}
    }
    

    This is how our SortedSet creation would look like using the above utility class in Lambda expression - 

    final SortedSet<Employee> employees = new TreeSet<Employee>(
    			(emp1,emp2) -> EmployeeUtils.compareEmployees(emp1, emp2)
    		);

    However, since we are just calling a static method in Lambda expression, it can easily be replaced by static method reference as follows -

    final SortedSet<Employee> employees = new TreeSet<Employee>(
    			EmployeeUtils::compareEmployees
    		);

    Looks crazy! But we can just replace whole Lambda expression with a static method reference. It automatically infers input parameters from functional interface (Comparator, in our case) and passes these to method being called. Method references use double colon (::) to call the methods.

    Instance Method References

    Just like static methods, we can replace Lambda expression with a instance method reference. Only difference will be that we may call method with an instance to call method on specific instance or class name to call method on any class instance. E.g. Let's consider that compare method is an instance method in class EmployeeComparator as follows - 

    import java.util.Comparator;
    
    public class EmployeeComparator implements Comparator<Employee>{
    	@Override
    	public int compare(Employee o1, Employee o2) {
    		if(o1 == o2) {
    			return 0;
    		}
    		final Long idDiff = o1.getId() - o2.getId();
    		if(idDiff == 0) {
    			return o1.getName().compareTo(o2.getName());
    		}
    		return idDiff.intValue();
    	}
    }

    We can now use following code to instantiate a TreeSet by passing instance method reference calling method on instance comparator.

    final EmployeeComparator comparator = new EmployeeComparator();
    		
    final SortedSet<Employee> employees = new TreeSet<Employee>(
    		comparator::compare
    	);

    In case, Employee implement Comparable interface and we want to use compareTo method, we will use a method reference using class name. It is important to note that we are calling instance method using class name, it will use the instance being passed as input parameter.

    In our use case, compareTo method will be called on first Employee parameter and second Employee parameter will be passed as input argument. Below code contains the same code using plain Lambda expressions and Method references -

    // Using Lambda expressions
    final SortedSet<Employee> employees = new TreeSet<Employee>(
    			(emp1, emp2) -> emp1.compareTo(emp2)
    		);
    
    // Using method reference
    final SortedSet<Employee> employees = new TreeSet<Employee>(
    			Employee::compareTo
    		);

    Constructor References

    This is similar to static or arbitary instance method references with method name as new. Syntax for constructor reference is <ClassName>::new.

    We will now be copying Employee objects from one SortedSet to another using constructor references as follows. In this, instead of saying new ConcurrentSkipListSet<Employee>(), we are using ConcurrentSkipListSet::new.

    import java.util.SortedSet;
    import java.util.TreeSet;
    import java.util.concurrent.ConcurrentSkipListSet;
    import java.util.function.Supplier;
    
    public class LambdaExpressions {
    	
    	public static void main(String[] args) {
    		
    		final SortedSet<Employee> employees = new TreeSet<Employee>(
    				Employee::compareTo
    			);
    		
    		transferEmployees(employees, ConcurrentSkipListSet<Employee>::new); // Constructor reference
    		
    	}
    	
    	private static void transferEmployees(SortedSet<Employee> source, Supplier<SortedSet<Employee>> target) {
    		//Logic to copy employees from source SortedSet to target SortedSet
    	}
    }

     

     

    Thank you for reading through the tutorial. In case of any feedback/questions/concerns, you can communicate same to us through your comments and we shall get back to you as soon as possible.

    Published on: 11th August 2016