Lamda expressions

 Lamda Expressions in Java:

Default Methods in Interface: clich here

  • without effecting implementation classes if we want to add new methods to interface then  we default methods
  • we can overide default methods in implementation classes or we can use deault implentation provided in interface it is up to you.
  • Default methods shold not be the methods of object classes.
  • Diamond problem can occur if a class implemts 2 inerfaces having same methods, the solution is discussed in above link.

Static Methods in Interface: click here

  • static methods are not part of implementation classes by deault.So you cannot override static methods in implentation classes.
  • Since it is not part of implementation classes, you cannot call it by creating object of implementation class.
  • Only way to call static method in interface is.InterfaceName.methodName()
  • To define general utily methods which never talks about objects we use static methods inside interface

Functional Interface: click here

  • An interface with excatly one abstract method is called fnational Interface
  • There can be any number of static or default methods.

Write Lamds Expressions:


Predefined Functional Interfaces:

Predicate:

interface Predicate<T t>{
public boolean test(T t){
}

  • We use predicate for condiional checks i.e it accepts any input type and returns boolen value only.
Eg: Check a number is even or not

     Predicate<Integer> p = num -> num%2==0;

  • we can call  above lamda expression using p.test(anyNum) any number of times.
  • Its possible to join predicates using and(), or() and negate() methods:
Eg:Print even numbers greater than 10

       Predicate<Integer> p1 = num -> num%2==0;

       Predicate<Integer> p2 = num -> num>10;

      p1.and(p2).test(anyInput)


Function:

interface Function<T t,R r>{
public R apply(T t){
}

  • We use function when you have a requirement to accept one input and return any value.
Eg:Find the length of a string

   Function<String,Integer> f = s -> s.length(); 

  • we can call above lamda expression any numer of times using f.apply("any string")
  • Other default methods  like andThen() & compose() are used for chaining i.e to use output of one method return value to the other.
Eg:Function<Integer,Integer> f1= i1->2*i;

     Function<Integer,Integer> f2= i2->i2*i2;

     1. f1.andThen(f2).apply(2)

             It will first perform f1 and then f2 i.e first doubles and then squares, so we get result as 16.

    2. f1.compose(f2).apply(2)

           It is quite opposite to above i.e first performs f2 and on that result it performs f1, so we get                     result  as 8.

Consumer:

interface Consumer<T t>{

public void accept(T t){

}

  • By the name we can guess that it accepts any input values but didn't return any thing i.e void. 

Eg: Print some thing to console

   Consumer<String > c=  str -> System.out.println(str);

  • we can call above lamda expression any numer of times using c.accept("any string")
  • It has only one default method used for chaining i.e andThen, which is used exactly above.

Supplier:


interface Supplier{
public R get();
}
  • By the name we can guess that it does not accepts any input values but  returns value.
Eg: Print date
  
   Supplier<Date> s= () -> new Date();
  • we can call above lamda expression any numer of times using s.get()
  • There are no other methods for chaining
 Summary:
 
 Functional Interface            InputType            ReturnType                 abstractMethod
      Predicate                              any                        boolean                             test
      Function                              any                         any                                   apply
      Consumer                            any                         void                                 accept                
      Suplier                                no input type           any                                   get

                                     BiFunctionalInterfaces

  • Above functional interfaces only accept one input parameter, below mentioned functional interfaces are exactly same as above ones but accepts 2 arguments.
  1. BiPredicate
  2. BiFunction
  3. BiConsummer

                                        Primitive Functional Interfaces

  • There are performance issues with normal functional Interfaces due to auto-boxing and unboxing which is auto mmatically performed by jvm, so we use primitive version of functional interfaces.
  • Example consider below example, if we want to perform square of number, then we pass in primitive type to apply function, but this interface accepts Integer, so autoboxing is performed by jvm and similary for permoning product operation aut0-unboxing is performed to convert back o int and finally the result is auto boxed to Integer.This is performance issue so we use primitive versions of same functional interfaces.
  •  Refer java8 documentation here
         Function<Integer,Integer> f= num -> num * num;
         f.apply(2);

       Primitve Functional Interfaces:
           
                                                                  Predicate
  
         1.IntPredicate
         2.DoublePredicate
         3.LongPredicate
   
      Eg:Since By the name we guess the input type, we no need to specify input type parameter to                  inerface.
       IntPredicate p= num-> num%2==0;
               
                                                                Function
 
  • Input type as mentioned in name and return type can be any
        1.IntFunction
        2.DoubleFunction
        3.LongFunction
  • Input type as mentioned before to  and return type after to
       1.IntToDoubleFunction
       2.IntToLongFunction
       3.DoubleToIntFunction
       4.DoubleToLongFunction
       5.LongToIntFunction
       6.LongToDoubleFunction

      Note: A small note here is abstract method changes here from apply to                                                       applyAsReturnType(returnType r) i.e for IntToDoubleFunction it is applyAsDouble(double d)

  • Input type can be any and return type after To
     1.ToIntFunction
     2.ToDoubleFunction
     3.ToLongFunction

       Note: A small note here is abstract method changes here from apply                                                         to  applyAsReturnType(returnType r) i.e  for ToDoubleFunction it is applyAsDouble(double d)

  • For Bi-Functions, inputs can be any types but return type menioned after to
   1.ToIntBiFunction
   2.ToLongBiFunction
   3.ToDoubleBiFunction

 Note: A small note here is abstract method changes here from apply                                                         to  applyAsReturnType(returnType r) i.e  for ToDoubleBiFunction it is applyAsDouble(double d)

Consumer
  • Input type as mentioned in the name return type is anyway  void.
  1. IntConsumer
  2. DoubleConsumer
  3. LongConsumer
  • for BiConsumer one arument is any thing and other is fixed as mentioned in name.
  1. ObjIntConsumer<T> - (void accept(T t,int value) )
  2. ObjLongConsumer<T>
  3. ObjDoubleConsumer<T>
Supplier
  • The return type 
  1. BooleanSupplier
  2. IntSupplier
  3. LongSupplier
  4. DoubleSupplier
 Note: The abstarct method is changed here as getAsReturnType i.e for BooleanSupplier it is getAsBoolean()


UnaryOperator
  • This is child interface of Function interface
  • It is just like function interface with both input and return type same.
  • even abstract methos remains same i.e apply()
Eg: To calculate square of number using normal function
       Function<Integer,Integer> f= num -> num* num;
       using its child unary operator
      UnaryOperator<Integer> f=num -> num*num;

Primitives of UnaryOperator:
  1. IntUnaryOperator
  2. LongUnaryOperator
  3. DoubleUnaryOperator
Note: Here the abstarct methos changes as applyaAsInt(int value) for IntUnaryOperator
  • The best way to write square of a number is 
  • IntUnaryOpeator f=num->num*num;
  • we can call the same using f.applyAsInt(3)
                                                                 BinaryOperator
  • This is child interface of Bi-Function interface
  • It is just like Bi-function interface with 2 input parametrs and return type also same.
  • even abstract methos remains same i.e apply()
Eg: To calculate sum of 2 numbers using normal Bi-Function
       BiFunction<Integer,Integer,Integer> f= (n1,n2) -> n1+n2;
       using its child unary operator
      BinaryOperator<Integer> f=(n1,n2) -> n1+n2;

Primitives of BinaryOperator:
  1. IntBinaryOperator
  2. LongBinaryOperator
  3. DoubleBinaryOperator
Note: Here the abstarct methos changes as applyaAsInt(int value) for IntBinaryOperator
  • The best way to write sum of 2 numbers is 
  • IntBinaryOpeator f=num->num+num;
  • we can call the same using f.applyAsInt(3,4)




















 
      













Comments