Master java skills

Java Lambda Expressions

A lambda expression is a way to implement the only abstract method in an interface (functional interface) without writing a separate class that implements that interface.

Lambda expressions are used to implement functional programming in java. Therefore, it is important to understand functional programming.

Functional Programming

Functional programming is a paradigm that allows programming using expressions. It means declaring functions, passing functions as arguments in a method and using functions as statements or expressions. Java8 calls them expressions.

Features of Lambda Expressions

  1. Lambda expressions are used with functional interfaces. They cannot be used with interfaces that have more than one abstract method.
  2. Using lambda expressions, we can directly pass an instance referred by a functional interface wherever it is required.
  3. We don’t have to create classes that implement a functional interface to create objects. We can use lambda expressions instead.
  4. Lambda expressions are used heavily while working with collections.

Note1 -> Lambda expression is a way of implementing the only abstract method in a functional interface without having a class that implements the functional interface.

Note2 -> Lambda expressions allows us to use interfaces without having their implementing classes.

Lambda expression syntax

//funtional interface method that takes just one parameter
(arg) -> {//code}                        //note that arg can be represented by any letter
(p) -> {//code}

//funtional interface method that takes just two parameters
(arg1, arg2) -> {//code}

//functional interface method that takes no parameter
() -> {code}

Lambda expression example

package com.javatrainingschool;

@FunctionalInterface
public interface Executable {
	
	void execute();

}
package com.javatrainingschool;

public class LambdaExpExample {
	
	public static void main(String[] args) {
		
		Executable executable = () -> {System.out.println("Executing...");};
		
		executable.execute();
		
	}

}
Output :
Executing...

Lambda expression with parameters

In the below example, functional interface method takes parameters. Calculate is an interface which takes two numbers. Based on the Lambda expression, we will decide what operation (summation, multiplication etc) we want to perform on those numbers.

package com.javatrainingschool;

@FunctionalInterface
public interface Calculation {
	
	int calculate(int num1, int num2);

}
package com.javatrainingschool;

public class LambdaExpExample {
	
	public static void main(String[] args) {
		
		Calculation sum = (n1, n2) -> (n1 + n2);
		int result = sum.calculate(10, 20);
		System.out.println("Result of sum = " + result);
		
                Calculation multiplication = (n1, n2) -> (n1 * n2);
		int multiplyResult = multiplication.calculate(10, 20);
		System.out.println("Result of multiplication = " + multiplyResult);
		
	}
}
Output :
Result of sum = 30
Result of multiplication = 200

Note -> If there is only one parameter in the method, () are optional. We can write (n) -> {//code} or simply n -> {//code}

Is return statement mandatory in lambda expressions?

In lambda expressions, if there is just one statement, then there is no need for a return statement. However, if still, someone wants to use it, he or she is free to do so. They only need to use curly braces{} for using return statement with single statement in lambda expression.

But when there are multiple statements, it is mandatory to use return statements.

//Lambda without return keyword in case of single statement
(a,b) -> (a+b);      

//Lambda with return keyword in case of single statement. Here curly braces are must
(a,b) -> { return (a+b); };

Lambda example with forEach() method

forEach() method takes Funcional interface Consumer as parameter. Consumer interface has just one method void accept(T t), where t represents the type of object Collection has.

Thus, we can use (t) -> {//code} as lambda here inside forEach() method as an argument. Or t -> {//code} can also be used since there is just one parameter that accept(T t) method takes.

package com.javatrainingschool;

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

public class LambdaWithForEachExample {
	
	public static void main(String[] args) {
		
		List<String> names = new ArrayList<>();
		names.add("Arjun");
		names.add("Shankar");
		names.add("Javed");
		
                //
below name represent one element in names list. It can be represented by a single
                //alphabet such as n or p also.
		names.forEach(name -> System.out.println("Name is : " + name));
			
	}
}
Output :
Name is : Arjun
Name is : Shankar
Name is : Javed

Lambda example with Runnable interface

Since Runnable interface also has just one abstract method, it qualifies as a SAM interface or functional interface. Therefore, we can use lambda with Runnable interface.

Here, we have provide implementation for method public void run() method. Thus, we can use () -> {} as lambda here.

package com.javatrainingschool;

public class RunnableWithLambdaExample {
	
	public static void main(String[] args) {
		
		Runnable runnable = () -> { System.out.println(Thread.currentThread().getName() + " running.");};
		
		Thread t1 = new Thread(runnable);
		t1.start();
		
		Thread t2 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " running.");});
		t2.start();
		
	}
}
Output :
Thread-0 running.
Thread-1 running.

Lambda example with Comparator interface

Comparator interface also a functional interface. It has one method public int compare(Object o1, Object o2). Thus, we can use (o1, o2) -> {//code} as lambda here.

package com.javatrainingschool;

public class Book {

	private int id;
	private String name;
	
	//getters and setters
        //parameterized constructor
        //toString()
	
}
package com.javatrainingschool;

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

public class LambdaWithComparatorExample {
	
	public static void main(String[] args) {
		
		List<Book> books = new ArrayList<>();
		
		books.add(new Book(1, "Master Java in 21 Days"));
		books.add(new Book(2, "HeadFirst Java"));
		books.add(new Book(3, "Learn Hibernate"));
		
		Collections.sort(books, (b1, b2) -> b1.getName().compareTo(b2.getName()));
		
		//The above statement can be written with return statement as well like below
		//Collections.sort(books, (b1, b2) -> { return b1.getName().compareTo(b2.getName()); });

                System.out.println(books);		
	}
}
Output :

[Book [id=2, name=HeadFirst Java], Book [id=3, name=Learn Hibernate], Book [id=1, name=Master Java in 21 Days]]

Lambda example to filter data in collections

We can also use lambda expression inside Stream apis filter method.

package com.javatrainingschool;

public class Book {

	private int id;
	private String name;
	
	//getters and setters
        //parameterized constructor
        //toString()
	
}
package com.javatrainingschool;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class LambdaWithForEachExample {
	
	public static void main(String[] args) {
		
		List<Book> names = new ArrayList<>();
		names.add(new Book(1, "Let Us C"));
		names.add(new Book(2, "Learn Java"));
		names.add(new Book(3, "HeadFirst Java"));
		names.add(new Book(4, "Learn Spring Boot"));
		
		Stream<Book> filteredStream = names.stream().filter(b -> b.getName().startsWith("H"));
		
		filteredStream.forEach(name -> System.out.println("Name is : " + name));
			
	}
}
Output :
Name is : Book [id=3, name=HeadFirst Java]