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

    }
}