Lambda-Functional Interfaces(Java-8)

Legitimate fair basic questions in a Java 8 beginner’s mind.

Every beginner is a hero.

Why Functional Interface is called a Functional Interface? What is so “functional” about it? Don’t all interfaces have functions / methods already? How is Functional Interface different from a normal Interface in Java? Can I just go on creating FIs (& why should I create those in the first place?) or Java8 has any Pre-Defined Functional Interfaces in its API? We all Java coders were happy calling all functions as methods in Pre-Java8. And for God’s sake, now all of a sudden we started calling methods as “functions”!! What’s the difference? What is the connection of LAMBDA Expression with Functional Interfaces? What is Lambda? What is Expression? Why Lambda is called LAMBDA? Is there any picture that would explain me everything from a bird’s eye view? There is something called “Stream API” in Java 8, what connection does it have with all this? Programmers often call SAMs (Single Abstract Methods) a higher order / first order functions, so what is so high order about it?

A picture serves 1000 words!

Mindmap
Java 8 with Lambda, Functional Interface & Stream API in perspective.

GET IT STRAIGHT!

Lambda keyword is derived from Lambda expression (->) used in programming language Scala which (like Javascript & Python) has derived the keyword lambda from Lambda Calculus which inherently comes from Mathematics where it is just a Greek alphabet Symbol (λ) indicating computation that has nothing to do with Lambda in Physics which means a wavelength. And Java 8 has adopted the expression of the “lambda” from Scala (I suppose to stay in the competition). Let’s see a code that uses Lambda Expression.

Smallest Java code using Lambda Expression

JDK 1.8
import java.util.*;
public class LambdaSmallestExample {
  public static void main(String args[]) {
    Arrays.asList(3.14, 2.1, 9.8).forEach(i->System.out.println(i));
  }
}

CODE OUTPUT

3.14
2.1
9.8

So what just happened here !?! What is “i” !! Why i is followed by Symbol (->) ?? Why that arrow is pointing to System.out.println() ? And why passing that i again in print command?

Left hand side of the arrow (lambda) i.e. “i” is the input parameter in an anonymous method belonging to an anonymous object of one of the Functional Interface Types. We use () round brackets in case more than 1 parameter.

e.g.

(i,j,k)->

“Please note passing multiple parameter here won’t work because the method pre-defined in Functional Interface takes only 1 parameter. When you have your own version of Functional Interface, you can have method accepting multiple parameters.

Right hand side of the arrow (lambda) i.e. System.out.println(i); is the line of instruction we provide to the same anonymous method. In short, we are providing the Java compiler bear minimum essentials that are required for constructing a method / function. We use {} curly brackets in case more than 1 line of instruction.

e.g.

-> {
    System.out.println(“i = ” + i);
    System.out.println(“j = ” + j);
    System.out.println(“k = ” + k);
}

So a little elaborated code would look something like this.

package j8.in.janisoftwares;
import java.util.Arrays;
import java.util.List;
//Just an ordinary class
public class LambdaElaboratedExample {
  public static void main(String args[]) {
    //Creating some collection.
    List<Double> val = Arrays.asList(3.14, 2.1, 9.8);
    //Usage of Lambda Expression ->
    //val.forEach(i->System.out.println(i));
    val.forEach(
      (i)->
      {
        System.out.println("------------------");
        System.out.println(i);
      }
    );
  }
}

CODE OUTPUT

------------------
3.14
------------------
2.1
------------------
9.8
// SO (SPEAKING IN TERMS OF PSUDO-CODE) ACTUALLY WHAT YOU ARE ESSENTIALLY SAYING HERE IS..

forEach(objOfSomeSAM_FI_Implementor);

//WHERE

java.util.function.SomeSAM_FI objOfSomeSAM_FI_Implementor = new SomeSAM_FI_Implementor();

//AND
someSAM_FI_Implementor implements java.util.function.SomeSAM_FI {
    samOfSomeSAM_FI (int i) {
        System.out.println(i); /*Coder's instruction given on right side of lambda expression.*/
    }
}

// SAM = Single Abstract Method
// FI = Functional Interface
samOfSomeSAM_FI = java.util.function.Consumer.accept(T t);
SomeSAM_FI = java.util.function.Consumer;

So no one (including compiler) would complain if we expose the working with implementation of java.util.function.Consumer type here, thereby which the code would look something like this.

package j8.in.janisoftwares;
import java.util.*;
import java.util.function.Consumer;
//Just an ordinary class
import java.util.*;
public class ConsumerInterfaceExample {
  public static void main(String args[]) {
    List<Double> val = Arrays.asList(3.14, 2.1, 9.8);

    /*
    Notice we implemented java.util.function.Consumer
    (one of the functional interfaces) as the
    anonymous inner class whose object reference "c"
    is passed in forEach();
    */
    Consumer<Double> c = new Consumer<Double>() {
      public void accept(Double i) {
        System.out.println("------------------");
        System.out.println(i);
      }
    };
    // Not passing method within a method. It's object only.
    val.forEach(c);
  }
}

CODE OUTPUT

------------------
3.14
------------------
2.1
------------------
9.8

Note that there is nowhere LAMBDA expression here. But we usually do not implement Consumer and directly pass objOfSomeSAM_FI_Implementor, do we? We passed with -> lambda symbol. So that saves coder from doing all that stuff which otherwise would have to do in order to carry out the same thing. And moment the compiler detects lambda expression, it instantly :

  • Treats left side of -> as input parameter.
  • Treats right side of -> as method body.
  • Constructs an anonymous method with these 2 ingredients.
  • Embeds that method and creates an anonymous class object of type Functional Interface (java.util.function.Consumer)
  • Passes that anonymous object to java.util.forEach(); for you.
  • And forEach() takes control from there.
  • It invokes the method for each element of Collection on which you have called forEach().

So do you always need to work with forEach() and java.util.function.Consumer to work with Lambda?

No. Not necessarily. You can very well have your own set of classes. All you need is a class that implements an interface that has just one single abstract method & then you are good to go for Lambda expression usage. The following code is not even implementing the SAM FI. It uses INNER CLASS do something of that sort.
package j8.in.janisoftwares;
/* Notice this class doesn't implement the interface. Just uses it. */
public class InterfaceDemo {
  public static void main(String[] args){
    // Traditional way.
    AbcImpl ai = new AbcImpl();
    ai.show();
    /* Implementation of interface Abc. via Anonymous inner class.
    Argument is: (Unlike AbcImpl) Why to define a named class when ...
    all that class does is, implement one single method. */
    Abc abc = new Abc() {
      public void show() {
        System.out.println("show() method invoked!");
      }
    };
    abc.show();
    /* And lambda further argues that:
      why to burden coder for even creating an anonymous class
      and why to even create a method when there is just 1 method!
      Smart lambda aspect of Java 8 will sense it.  */
    Lmb lmb = () ->
    {
      System.out.println("Anonymous code invoked via Lambda.");
    };
    lmb.showLambda();
    /* Moreover, you don't need curly bracket if block has just 1 statement.
    So that is also removed.
    That's why the look of this seemingly cryptic code.
    But as they say, boiler plate code is removed. */
    Lmb lmb2 = () -> System.out.println("Anonymous code [with no parenthesis] invoked via Lambda");
    lmb2.showLambda();
  }
}
interface Abc {
  void show();
}
interface Lmb {
  void showLambda();
}
/* Traditional way of implementing interface. */
class AbcImpl implements Abc {
  public void show() {
    System.out.println("Traditional way implementation.");
  }
}

CODE OUTPUT

Traditional way implementation.
show() method invoked!
Anonymous code invoked via Lambda.
Anonymous code [with no parenthesis] invoked via Lambda

Did you know? “Lambda Expression” is as useless without a Functional Interface as a TV-Remote-Control is without TV or a scoop spoon is without ice-cream. Hence, it promotes Functional Programming.

If no TV, then no REMOTE CONTROL. Just like how good is a scooping spoon if there is no Ice Cream! You can take a dab of ice cream somehow by any other means. Same way, you CAN very well invoke SAM of FI in a conventional way but if no FI then no LAMBDA EXPRESSION.

REMEMBER !!!
Not all the interfaces make Lambda Expression useful. 
There were 2 types of interfaces in Java until Java 1.8
1. Regular Interface with multiple methods declared.
2. Marker Interface with nothing in it. Just a name (e.g. Serializable)
3. SAM (Single Abstract Method) Interface that has just one & "ONLY 1" abstract method. This is your FI. (Java 8 feature)
So Lambda works only with FIs.

So let’s see Functional Interface (FI) now. Any Java interface with single abstract method (SAM) is potentially eligible to be called a functional interface. (although Java8 has pre-defined Functional Interfaces in its API, namely: Predicate, Consumer, Supplier however …) I wrote the codes specially for a Java 8 beginner which itself carries sufficient self explanatory code comments (Please read them). Just copy paste and run! This code has nothing to do with Predicate, Consumer or Supplier jargon. Just a plain Functional Interface. And methods in it can be invoked by normal way however, it can be invoked by Lambda Expression too.

This basic simple example of functional interface essentially demonstrates:

  • Annotation (optional) feature & its significance.
  • Implementation and using of:
    • Multiple default methods.
    • Multiple static methods.
    • SAM (Single Abstract Method).
      • Invoking SAM via:
        • Regular way of normal object creation.
        • Anonymous class object.
        • Lambda Expression (Anonymous class and anonymous method).
package j8.in.janisoftwares;
class FunctionalInterfaceExample {
  public static void main(String[] args) {
    FInterface.myStaticMethod1(); //Simple call via static reference.
    FInterface fi1 = new A();
    fi1.myDefaultMethod1(); //Simple call via instance reference.
    fi1.mySAM(); // Simple call via instance reference.
    FInterface fi2 = new A () {
      public void mySAM() {
        System.out.println("mySAM invoked via anonymous inner class");
      }
    };
    fi2.mySAM();
    FInterface fi3 = () -> {
      System.out.println("mySAM invoked via Lambda Expression");
    };
    fi3.mySAM();
  }
}
/* Annotation Optional. Tells compiler to prevent coder from adding 1 more SAM */
@FunctionalInterface
interface FInterface {
  public void mySAM();
  /*
  Java8 allows SAM Interface (Functional Interface) to have
  multiple static methods.
  */
  public static void myStaticMethod1() {
    System.out.println("my static method 1 invoked!");
  }
  static void myStaticMethod2() {
    System.out.println("my static method 2 invoked!");
  }
  /* Java8 allows SAM Interface (Functional Interface) to have
  multiple default methods. */
  default void myDefaultMethod1() {
    System.out.println("my default method 1 invoked!");
    // Invoking static method in default method of that interface is possible.
    myStaticMethod2();
  }
  default void myDefaultMethod2() {
    System.out.println("my default method 2 invoked!");
  }
} //SAM Interface ends here.
/* Implements a functional interface. */
class A implements FInterface {
  public void mySAM() {
    System.out.println("my SAM invoked!");
    /*  Invoking default method of SAM interface
    within implementor class is possible. */
    myDefaultMethod2();
  }
}

CODE OUTPUT

my static method 1 invoked!
my default method 1 invoked!
my static method 2 invoked!
my SAM invoked!
my default method 2 invoked!
mySAM invoked via anonymous inner class
mySAM invoked via Lambda Expression

And finally, let’s see how can we use Lambda Expression for SAM FIs that are there in Java 8 API. Try this code. Following code also demonstrates various (old and new) ways of treating collection. It walks you from traditional through new approach. Enjoy.

package j8.in.janisoftwares;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
class lambda_1 {
  public static void main(String[] args) {
    //ArrayList collection.
    ArrayList<Integer> al = new ArrayList<Integer>();
    al.add(1);
    al.add(2);
    al.add(3);
    al.add(4);
    //List collection.
    List<Integer> lst = Arrays.asList(11,22,33,44,55);
    System.out.println("OLD TRADITIONAL WAY - for(int i=0;i<size; i++)-----");
    for (int i = 0; i < al.size(); i++) {
      System.out.print(al.get(i) + " ");
    }
    System.out.println();
    for (int i = 0; i < lst.size(); i++) {
      System.out.print((Integer)lst.get(i) + " ");
    }
    System.out.println("\nfor(Integer i : collectionRef) WAY ------------");
    for (Integer i : al) {
      System.out.print(i+ " ");
    }
    System.out.println();
    for (Integer i : lst) {
      System.out.print(i+ " ");
    }
    System.out.println("\nLAMBDA way with a parameter in parenthesis");
    al.forEach((i)->System.out.print(i+ " "));
    System.out.println();
    lst.forEach((i)->System.out.print(i+ " "));
    System.out.println("\nLAMBDA way without parenthesis");
    al.forEach(i->System.out.print(i+ " "));
    System.out.println();
    lst.forEach(i->System.out.print(i+ " "));
  }
}

CODE OUTPUT

OLD TRADITIONAL WAY – for(int i=0;i<size; i++)—–
1 2 3 4
11 22 33 44 55
for(Integer i : collectionRef) WAY ————
1 2 3 4
11 22 33 44 55
LAMBDA way with a parameter in parenthesis
1 2 3 4
11 22 33 44 55
LAMBDA way without parenthesis
1 2 3 4
11 22 33 44 55


CAUTION: Things now getting little ugly as moving from user defined FIs to Java 8 pre-defined FIs.

Package java.util.function

Please notice “java.lang.Iterable.forEach(<some parameter here>)” utility method that takes entire anonymous class object via lambda as parameter. Well! what kind of object does it take in? Obviously java.util.function.Consumer type of object because it has to wrap it and internally invokes java.util.function.Consumer.accept() whose implementation code body is supplied by the coder himself. It is exactly an instruction “WHAT IS TO BE DONE”. And Iterable.forEach() along with Consumer.accept() figures out how it is to be done. In samples codes we do nothing but simply display the values.

Function, Predicate, Supplier, Consumer. “This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference”. [https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html, 25th July 2021]

We’ve seen Consumer examples a lot. Let’s take a peep into Supplier functional interface.

java.util.function.Supplier Examples

java.util.function.Supplier.get()
Takes no input. Gives output.

package j8.in.janisoftwares;
/*
  All in one example that demonstrates working of
  Functional Interface SUPPLIER in various ways.
*/
 
import java.util.function.Supplier;
public class FISupplier {
    public static void main(String[] args) {
 
      System.out.println("Random via ordinary Math.random(): " + Math.random());
 
      /*
        Random just works fine like this.
        Why do I need to put it in Supplier / Lambda then?
 
        Agreed. But we are not here to dmonstrate random.
        Idea here, is to supply some method body that processes some instructions
        and then see Supplier.get() returning something.
      */
 
      /*
        Standard way of doing it is as below through lambda assignment.
      */
      Supplier<Double> r = ()->Math.random();
      System.out.println("Random via Lambda Expression: " + r.get());
 
      /*
        You could accomplish same results via implementing get() method
        of Supplier FI by making an anonymous inner class as below.
      */
        Supplier<Double> randomValue = new Supplier<Double>() {
          public Double get() {
            return Math.random();
          }
        };
        System.out.println("Random via Supplier.get() Anonymous Implementation: " + randomValue.get());
 
        /*
          Traditional way of externalizing the implementation and passing object explicitly.
          No anonymous class here.
        */
        Supplier sup = new SupplierFI_Impl();
        System.out.println("Random via Supplier.get() Explicit Implementation: " + sup.get());
    }
}
 
/*
  Implements java.util.function.Supplier interface.
*/
class SupplierFI_Impl implements Supplier {
  public Double get() {
    return Math.random();
  }
}

CODE OUTPUT

Random via ordinary Math.random(): 0.7558628623878451
Random via ordinary Math.random(): 0.09620883117469159
Random via Lambda Expression: 0.6444300001982375
Random via Supplier.get() Anonymous Implementation: 0.455978812437302
Random via Supplier.get() Explicit Implementation: 0.3021814410740643

Having looked at Consumer & Supplier, now it is time to see some Predicate examples.

java.util.function.Predicate Examples

boolean java.util.function.Predicate.test(T t). Takes input, evaluates and returns boolean (true / false).
package j8.in.janisoftwares;
import java.util.function.Predicate;  
public class LambdaPredicateExample {  
    public static void main(String[] args) {  
        Predicate<Integer> predicate = testVal -> (testVal > 555);
        System.out.println(predicate.test(10));
        System.out.println(predicate.test(555));
        System.out.println(predicate.test(556));
    }  
}

/*
 * As you can see there is no implementation of test() here.
 * Just a direct call to test() via lambda.
 * However, there are java APIs that act as wrapper method & 
 * internally implement test().
 * 
 * Just like how forEach() wraps around Consumer.accept() 
 *  
 */

CODE OUTPUT

false
false
true

We understood what is LAMBDA, we understood how LAMBDA works. The biggest confusion still lies, “how to know how to use, when to use, where to use”. So here is a cocktail telling in what different ways can we use lambda expression all jammed in one code as below.

package j8.in.janisoftwares;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class PredicateConsumerSupplierExample {

    public static void main(String[] args) {
    	
    	//PREDICATE [test()]
    	System.out.println("\nLAMBDAing java.util.function.Predicate.test()");
        Predicate<Integer> predicate = testVal -> (testVal > 555);
        System.out.println(predicate.test(556));
        
        //CONSUMER [accept()]
        System.out.println("\nLAMBDAing java.util.function.Consumer.accept()");
        List<Double> val = Arrays.asList(3.14, 2.1, 9.8);
        Consumer consumer = i -> System.out.println(i);
        val.forEach(consumer);
        
        //SUPPLIER [get()]
        System.out.println("\nLAMBDAing java.util.function.Supplier.get()");
        Supplier<Double> supplier = () -> Math.random();
        System.out.println(supplier.get());
    }
}

Let’s see Stream API of Java 8 that makes use of Lambda Expression

package j8.in.janisoftwares;
import java.util.stream.*;
public class StreamExample {
	public static void main(String[] args) {
		Stream.iterate(1, e -> e + 1)
		.filter(e -> e %5 == 0)
		.limit(10)
		.forEach(e -> System.out.println(e));
	}
}
/*
* Iteration starts with increment by 1.
* Filters only those numbers that are divisible by 5.
* Limit to go for 10 such numbers.
* print each number.
*/

CODE OUTPUT

5
10
15
20
25
30
35
40
45
50

HONEYMOON IS OVER. Time to get facts checked.

  • Java was, is and will remain Object Oriented. No matter how many fantastic jargon come up to confuse.
  • Java Developer code is at mercy of JAVA API (e.g. stream, Map, etc…) for using Lambda Expression unless developer is creating our own API which internally implements Functional Interfaces.
  • Lambda expression is at mercy of Functional Interfaces. No Functional Interface usage, no lambda expression usage.

As we all know Java is platform independent because it is portable, I have tested in different environments both Linux Ubuntu O/S with ATOM editor, JDK 11, as well as Windows O/S, Eclipse-Oxygen, JDK 1.8 It works just fine.


My special thanks to my friend & ex-colleague John Wilfred (Sydney, Australia, jenrise.com/blog) who has been a great support like a big brother to me in my quest for Lambda and learning new Java 8 features including Functional Programming. I dedicate this BLOG to John. If this content makes him proud, it’s worth.


Advertisement