When to use Lambda Expressions
If you have a functional Interface which has only one Abstract Method then we can use Lambda Expression at the Class which implements the functional interface
and provides the method definition for the abstract method in Functional Interface.
- We have a Function Interface Printable with abstract method print
- Two classes implements functional Interface – DailyReport and MothlyReport. They provide the method definition for Print() method in Printable Interface
- GenerateReport class has a downloadReport method which takes the Printable Type as argument
- DownloadReport Class we use the Printable reference and call the exact implementation by passing as argument
Printable.java
@FunctionalInterface public interface Printable { public void print(); }
DailyReport.java
public class DailyReport implements Printable { @Override public void print() { System.out.println("Printing Report in Daily Format"); } }
MonthlyReport.java
public class MonthlyReport implements Printable { @Override public void print() { System.out.println("Printing Report in Monthly Format"); } }
GenerateReport.java
public class GenerateReport { public static void downloadReport(Printable printable){ printable.print(); } }
Without Lambda Expression
DownloadReport.java
public class DownloadReport { public static void main(String[] args) { GenerateReport objGenReport = new GenerateReport(); Printable objDailyReport = new DailyReport(); Printable objMonthlyReport = new MonthlyReport(); objGenReport.downloadReport(objDailyReport); objGenReport.downloadReport(objMonthlyReport); } }
Output
Printing Report in Daily Format Printing Report in Monthly Format
With Lambda Expression
Using Lambda expression we can pass method definition directly as parameter instead of using a class to extend interface and writing a method definition for the interface method
DownloadReport.java
public class DownloadReport { public static void main(String[] args) { GenerateReport objGenReport = new GenerateReport(); objGenReport.downloadReport(() -> System.out.println("Printing Report in Daily Format using Lambda Expr")); Printable mnthlyReport = () -> System.out.println("Printing Report in Monthly Format using Lambda Expr"); objGenReport.downloadReport(mnthlyReport); } }
Output
Printing Report in Daily Format using Lambda Expr Printing Report in Monthly Format using Lambda Expr
With Lambda Expression | Without Lambda Expression |
We pass the method definition as Argument |
|
Let’s take the below Example where we find the square of even numbers
ReduceEgs.java
public class ReduceEgs { public static void main(String[] args) { List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6); arrNumbers.stream() .filter(x-> x%2==0) .map(x->x*x) .forEach(System.out::println); } }
In the above code we have
- Filter which takes Predicate as Param
- Map which takes Function as Param
- Sysout which takes Consumer as Param
The filter, Consumer and sysout have been expanded from the above code as below.
public class ReduceEgs { public static void main(String[] args) { List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6); arrNumbers.stream() .filter(getIntegerPredicate()) .map(getFunction()) .forEach(getPrintln()); } private static Consumer<Integer> getPrintln() { return System.out::println; } private static Function<Integer, Integer> getFunction() { return x -> x * x; } private static Predicate<Integer> getIntegerPredicate() { return x -> x % 2 == 0; } }
The Above code is refactored as below
public class ReduceEgs { public static void main(String[] args) { List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6); Function<Integer, Integer> getSquare = x -> x * x; Predicate<Integer> getEvenNo = x -> x % 2 == 0; Consumer<Integer> showNos = System.out::println; arrNumbers.stream() .filter(getEvenNo) .map(getSquare) .forEach(showNos); } }
What the java compiler does internally changes the above code as below. It created an anonymous inner class for implementing the methods in the interface.
- Function has apply method which should be implemented
- Consumer has accept method which should be implemented
- Predicate has test method which should be implemented
public class ReduceEgs { public static void main(String[] args) { List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6); Function<Integer, Integer> getSquare = new Function<Integer, Integer>() { @Override public Integer apply(Integer integer) { return integer * integer; } }; Predicate<Integer> getEvenNo = new Predicate<Integer>() { @Override public boolean test(Integer integer) { return integer % 2 == 0; } }; Consumer<Integer> showNos = new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }; arrNumbers.stream() .filter(getEvenNo) .map(getSquare) .forEach(showNos); } }
The output of all the above code is one and same as below
4 16 36