Lambda Expressions & Functional Interface

3 minute read

Lambda

Lambda Expression is just an anonymous (nameless) function. A function does not have a state. Object has a state.

Best Practices

  1. Be declarative and less imperative
  2. Favor immutability
  3. Reduce side effects
  4. Expressions over statements
  5. Design with Higher-Order functions

Functional Interface

  • Interface with SAM : Single Abstract Method
  • Functional Interface : can automatically be elevated to lambda expression. In other words, you can only use lambdas for functional interfaces
  • Functional interface assign a contract!!
View Functional Interfaces

Writing Lambda

A function/method has 4 parts : name, return type, params/args list & method body Parts of a Method/Function

The most important parts are just the arguments and body. In Functional interface, there is only one method, so the name of the method is implied and the override has to be done by any class implementing the method.

Lambda with parameter/argument data type Image Text

even the argument/parameter data type can removed as Image Text

Spotting Valid Lambdas

Defining Lambda : Providing implementation to the abstract method

y = (arg1, arg2) -> arg1 + arg2;
  • RETURN Always need curly braces and ends with a colon
  • Without Curly braces we can’t use return keyword
     n->{n*n;};//INVALID, no return statement.
      
     c -> return 10; //DOES NOT COMPILE : return keyword without {}
     c -> { return 10; } //CORRECT
       
    a -> {
        return a.startsWith("test")
        }//DOES NOT COMPILE : need ; after return
    a -> {
       return a.startsWith("test");
     }//CORRECT
    
  • multiple parameters need to be enclosed in the brackets
    a,b -> a.startsWith("Ni")//DOES NOT COMPILE : need small brackets
    CORRECT: (a,b) -> a.startsWith("Ni")
    
  • Data types for the input parameters of a lambda expression are optional
    (int y, z) -> { int x = 1; return x+y; }// DOES NOT COMPILE : Either both have data types or none
    ( y, z) -> { int x = 1; return x+y; } // CORRECT
     (int y, int z) -> { int x = 1; return x+y; }//CORRECT:
    
    (a,b) -> { int a = 9; return a+b }//DOES NOT COMPILE: Redeclaration of a
    (a,b) -> { int c = 9; return a+b }// CORRECT AS C is an independent local variable
    
  • VALID Lambdas
    MyFunctionalInterface t;
    t = () -> true; //ZERO Parameter, return Boolean
    t = a -> {return a.startsWith("Ni");}
    t = (String a) -> a.startsWith("Ni")
    t = (int x) -> {} //One parameter and no function body
    t = (int y) -> {return;}
    

Method Accepting Lambda

Any method that accepts Functional Interface as parameter, needs a Lambda, For Example forEach accepts a Consumer (a functional interface) as a parameter.

default void forEach(Consumer<? super T> action);

Traditionally the anonymous class implementation is done :-

List<Integer> list = Arrays.asList(1,4,6,8,9,7,5,3,2);

//Implemenation via anonymous inner class
list.forEach(new Consumer<Integer>() {
    @Override
    public void accept(Integer i) {
        System.out.println(i);
    }
});

Above can be reduced by just keeping only the args and the method body

list.forEach((Integer i) -> {
        System.out.println(i);
        return;
    });
//i -> params/args & System.out.println(i) -> body

This can further be reduced by removing data type from argument, and removing unnecessasary return statement

list.forEach(i -> System.out.println(i));

This can be further reduced with the usage of method reference

list.forEach(System.out::println);

For better understanding, step by step declaration and usage can be tried.

Consumer consumer = x -> System.out.println(x);//since for each accepts a consumer, declare it first
//use the consumer with the method present in the Consumer interface
list.forEach(val -> consumer.accept(val));
//Or for simplicity just pass the consumer
list.forEach(consumer);
//or just replace the variable
list.forEach(x -> System.out.println(x));

Strategy Pattern

Strategy pattern. writing a function to be called as Lambda

Consider a method written in such a way that it accepts a functional interface as an argument.

Just pass a Lambda as parameter

Since the strategy can be decided at runtime, we can pass the strategy right at the time when its needed

List<Integer> values = Arrays.asList(1, 2, 3, 4, 5, 6);

//Print sum of all numbers
System.out.println(totalValues(values, e -> true));

//Print sum of all all Even numbers
System.out.println(totalValues(values, e -> e % 2 == 0));

//Print sum of all odd numbers
System.out.println(totalValues(values, e -> e % 2 != 0));
  • Lambda expression can access static variables, instance variables,
  • effectively final variables and effectively Final local variables