Lambda Expressions
In this blog, I have explained Lambda Expressions and Anonymous Inner classes in brief. first, I will go through the example without using the above 2 . then will implement the same with Lambda Expressions and Anonymous Inner classes.
Will go through the filtering example. which performs filtering on the Price of the Hotel and Star Ratings
interface filtering{
// Hotel is some POJO Class
boolean test(Hotel hotel);
}
//class which implements the above interface filtering
class FilteringOnPrice implements filtering
{
public boolean test(Hotel hotel)
{
if(hotel.getPrice()<5000)
return true;
else
return false;
}
}
//class which implements the above interface filtering
class FilteringOnStraRating implements filtering
{
public boolean test(Hotel hotel)
{
if(hotel.getStarRating().equals("5 Star"))
return true;
else
return false;
}
}
//In this class we will write actual filtering logic
class FilteringService
{
public void filterHotels(Filtering filtering){
List<Hotel> filteredHotels=new ArrayList<>();
List<Hotel> list = getHotelsList();
for(Hotel hotel: list){
if(filtering.test(hotel)){
filteredHotels.add(hotel);
}
}
}
}
class Main
{
public static void main(String args[]){
FilteredService filteredService=new FilteredService();
List<Hotel> hotelsByPrice =
filteredService.filterHotels(new FilteringOnPrice());
List<Hotel> hotelsByRating =
filteredService.filterHotels(new FilteringOnStarRating());
}
}
In the above example passing specific object as arguement will call appropriate method and perform filtering. the same can be achieved by Anonymous inner class we can write an actual implementation of the method as argument instead of writing in a separate class
By using Anonymous inner class
interface Filtering{
// Hotel is some POJO Class
boolean test(Hotel hotel);
}
//In this class we will write actual filtering logic
class FilteringService
{
public ArrayList<Hotel> filterHotels(Filtering filtering){
List<Hotel> filteredHotels=new ArrayList<>();
List<Hotel> list = getHotelsList();
for(Hotel hotel: list){
if(filtering.test(hotel)){
filteredHotels.add(hotel);
}
}
return filteredHotels;
}
}
class Main
{
public static void main(String args[]){
FilteredService filteredService=new FilteredService();
List<Hotel> hotelsByPrice=
filteredService.filterHotels(new Filtering(){
public boolean test(Hotel hotel)
{
if(hotel.getPrice()<5000)
return true;
else
return false;
}
});
List<Hotel> hotelsByRating=
filteredService.filterHotels(new Filtering(){
public boolean test(Hotel hotel)
{
if(hotel.getStarRating().equals("5 Star"))
return true;
else
return false;
}
});
}
}
In the above Main class we have directly written actual implementation of test method inside anonymous Inner class . So Internally JVM will understood and assign this method to the test method in the filtering Interface by comparing return type and number of Arguements. the same can be acheived by using Lambda Expression.
By using Lambda Expression
interface Filtering{
// Hotel is some POJO Class
boolean test(Hotel hotel);
}
//In this class we will write actual filtering logic
class FilteringService
{
public ArrayList<Hotel> filterHotels(Filtering filtering){
List<Hotel> filteredHotels=new ArrayList<>();
List<Hotel> list = getHotelsList();
for(Hotel hotel: list){
if(filtering.test(hotel)){
filteredHotels.add(hotel);
}
}
return filteredHotels;
}
}
class Main
{
public static void main(String args[]){
FilteredService filteredService=new FilteredService();
List<Hotel> hotels=
filteredService.filterHotels(
(Hotel hotel) ->
{
if(hotel.getPrice()<5000)
return true;
else
return false;
}
);
List<Hotel> hotels=
filteredService.filterHotels(new Filtering(){
(Hotel hotel) ->
{
if(hotel.getStarRating().equals("5 Star"))
return true;
else
return false;
}
});
}
}
Functional interface :
If the interface contains a single abstract method then that Interface is called Functional Interface and it is annotated with @FunctionalInterface
Note: Anonymous Inner class can be implemented even if the abstract class has more than one method. but whereas with Lambda expression it's not possible. we can write the implementations for the methods in Functional Interface only
Comparator Interface :
Method 1: Implementing Comparator Interface by using Lambda Expression
Comparator<Student> comp=(s1,s2)->(s1.id<s2.id)?-1:(s1.id>s2.id)?1:0;
Collection.sort(al,comp);
for(Student st:al){
System.out.println(st.id+" "+st.name+" "+st.country);
}
Method 2: Implementing Comparator Interface by using Anonymous Inner class
Comparator<Student> comp =new Comparator<>(){
public int compare(Student s1,Student s2){
return s1.id-s2.id;
}
};
Collection.sort(al,comp);
for(Student st:al){
System.out.println(st.id+" "+st.name+" "+st.country);
}
Context of this Keyword :
- if this key word is used in Lambda expression it does not refers to anonymous inner class it refers to the class that Lambda expression actually present
Value Capture
Modification of local variables is not allowed. internally compiler will take it as final , out side lambda expression also we cant modify . The reason is when it returned function is popped out from the stack . the variable has no meaning . so compiler has captured and replaced with the variable already
we can modify ArrayList as the reference is in stack but we can reinstatiate ArrayList again
class Main
{
String name;
public Filter testLambda()
{
FilteredService filteredService=new FilteredService();
this.name="sunil"; // here this actually refers to main class
int price=5000;
List<Hotel> hotels=
price++;
ArrayList<Integer> al=new ArrayList<>();
Filter filter = (Hotel hotel) ->
{
al.add(10)// this is allowed
if(hotel.getPrice()<price)
return true;
else
return false;
}
return filter;
}
public static void main(String args[]){
Main obj=new Main();
Filter filter= obj.testLambda();
filter.test(hotelObject);
}
}
Predicate :
Predicate is a functional interface which has a test method which accepts 1 argument and returns a boolean value
here in below example replaced Filter with Predicate . now there is no need of that Filter interface we can directly use predicate
class Main
{
String name;
public Predicate<Hotel> testLambda()
{
FilteredService filteredService=new FilteredService();
this.name="sunil"; // here this actually refers to main class
int price=5000;
List<Hotel> hotels=
price++;
ArrayList<Integer> al=new ArrayList<>();
Predicate<Hotel> filter = (Hotel hotel) ->
{
al.add(10)// this is allowed
if(hotel.getPrice()<price)
return true;
else
return false;
}
return filter;
}
public static void main(String args[]){
Main obj=new Main();
Predicate<Hotel> filter= obj.testLambda();
filter.test(hotelObject);
}
}
Combined Predicates:
we can use combination of 2 predicates to build some functionality by using and operation
class Main
{
public static void main(String args[]){
Predicate<Integer> divisibleBy2= (x)->{return x%2==0;}
Predicate<Integer> divisibleBy3= (x)->{return x%3==0;}
Predicate<Integer> divisibleBy6= divisibleBy2.and(divisibleBy3);
boolean check= divisibleBy6.test(36);
}
}
Consumer :
it is a functional interface that has method accept its return type is void , its just consume but does not return any thing. it has a method accept
Combining Consumer :
we can combine two consumers by using andThen method
class Main
{
public static void main(String args[]){
List<Integer> list= List.of(1,2,3,4,5);
Consumer<Integer> consumer= (a)->System.out.println(a);
list.forEach(consumer);
Consumer<Integer> consumer1= (a)->System.out.println(a);
Consumer<Integer> consumer2= (a)->System.out.println(a);
Consumer<Integer> consumer3= consumer1.andThen(consumer2);
list.forEach(consumer3);
}
}
Supplier :
Supplier is a functional interface which doesnot take any arguement but return some value. it has a method get()
class Main
{
public static void main(String args[]){
Supplier<Double> supplier = ()->Math.random();
System.out.println(supplier.get()):
}
}
Function :
Function is a functional interface . which has method apply . it takes 1 arguement and returns another argument.
class Main
{
public static void main(String args[]){
Function<String,Integer> strToInt = str->str.length();
System.out.println(strToInt.apply("Sunil")):
}
}
Method References:
Rules for method References
(arg)->ClassName.staticMethod(arg); is equal to ClassName::staticmethod
(arg)->ClassName.staticMethod() is equal to ClassName::staticmethod
(String s)->Integer.parseInt(s); is equivalent to Integer::parseInt
()->new ArrayList<>(); is equivalent to ArrayList::new
()->new Student(); is equivalent to Student::new
class Main
{
public static void main(String args[]){
List<Integer> list= List.of(1,2,3,4,5);
System.out.println(list.forEach(System.out::println);
// (arg)->ClassName.staticMethod(arg);
//is equal to ClassName::staticmethod
List<String> alt= List.of("sunil" , "kiran", "abcd");
al.sort((s1,s2)->s1.compareToIgnoreCase(s2) )
al.sort(String::compareToIgnoreCase)
// both are equivalent
}
}