Lambda Expressions in Java


Lambda expressions were added in Java 8 along with functional programming. Before understanding lambda expressions or lambdas for short, a prerequisite is to understand what is a Functional Interface. A functional interface is nothing but a simple interface with a single abstract method in it.
Example of a Functional interface is as follows:

interface SampleFunctionalInterface {
   abstract void abstractMethod();
}

An important thing to note that other standard methods and default methods, are also allowed in a functional interface.
Example as follows:

interface AnotherFunctionalInterface {
    abstract void abstractMethod();

    void normalMethod(){
        //implementation goes here
    }

    default void defaultMethod(){
        //default implementation goes here
    }
}

Learning about functional Interface was a piece of cake, right?! Let's move ahead to know about a lambda. For that, let's go through the following code:

/* A class to represent animal with different traits */
class Animal {
    private String name;
    private boolean canHop;
    private boolean canSwim;
    public Animal(String name, boolean canHop, 
    boolean canSwim){
        this.name = name;
        this.canHop = canHop;
        this.canSwim = canSwim;
    }
    boolean canHop(){
        return canHop;
    }
    boolean canSwim(){
        return canSwim;
    }
}
    
/* A functional interface to test traits of animals */  
interface TestTraits{
    boolean test(Animal a);
}

/* A class that provides implementation to test hopping trait of an animal */
class TestCanHop implements TestTraits{
    boolean test(Animal a){
        return a.canHop();
    }
}    

/* A class that represents a researcher who wants to test certain animals for certain traits */
class Researcher(){     
    public static void main(String[] args){
        List<Animals> animals = new ArrayList<>();
        animals.add(new Animal("cat", true, false));
        animals.add(new Animal("turtle", false, true));
        printHoppers(animals, new TestCanHop());
    }

    /* A method which prints animals which can hop */
    void printHoppers(Animal a, TestTraits testTraits){
        for (Animal a : animals){
            if(testTraits.test(a)){
                System.out.println(a);
            }
        }
    }
}

Using the above class TestCanHop, our Researcher could determine that from given animals, a cat can hop. Now the Researcher wants to identify animals which can swim. So, how should we change our code?

We will need to create another class TestCanSwim, which implements TestTraits—followed by creating another method printSwimmers in Researcher class. Not a lot of work, right?! But what if our curios researcher whats to explore animals for a lot of other traits.
Is there a way to avoid the effort of creating an entire class to implement a single method from an interface? Lambda expressions are our saviors!

This is how printHoppers method look now:

printHoppers(animals, new TestCanHop());

Upon using lamdba it looks like this:

printHoppers(animals, a -> a.canHop());

Does this a -> a.canHop() look similar to method body of
test method from TestCanHop ?

You got it right! Using lambda, we eliminated the entire method and thus need to create a class. As you see, code elimination is the apparent benefit of using lambda.

Now, the printHoppers method was expecting an instance of
TestTraits interface. But we pass this funky looking lambda in it, so how does it work? Well, Java does the work for us!
Java maps the lambda to the Interface, since there is only one method without any implementation, a.k.a abstract method in the Interface, a.k.a Functional interface, the mapping is evident for Java.

Here's how the syntax of lambda looks like:
Screenshot-2020-03-26-at-17.04.59

Here's the detailed syntax of a lamdba expression:
Screenshot-2020-03-26-at-17.05.04

As you can see, the arrow is used to separate the method parameters and method body of test method from TestCanHop class. Thus, by using lambdas, we improve code readability.

In a nutshell, a lambda is a minimal way to write a method. It is an expression that can be passed as a method parameter. They are also known as 'anonymous functions.' Given the fact that despite being a method or function, it does not have a name.

Now there are specific rules for writing this lambda expression:

  1. Method parameters and local variables in lambda are not allowed to be modified.
    For example, this lambda is invalid and does not compile.
(a, b) -> {a=1; return b;}
  1. Additionally, other variables such as instance variables and static variables are accessible from within lambda.
  2. If there is more than one parameter, we need to write them in parenthesis.
  3. Similarly, if there is more than one sentence in the method body, we need to write them in curly braces.
  4. If we are using curly braces, it should be a valid code block. Thus, we need to write a 'return' keyword and give semicolon after every sentence.
  5. Note the optional items for a lambda; a parameter type is optional. Curly braces are optional if the method body contains only one sentence. (This rule is common in Java, also applies to if/else structure and loops.)

Let's go through above rules once again before trying to solve the following question.

Question

To get used to the lambda syntax, lets find which one these are valid lambdas:

option a: print((String a, String b) -> a.startsWith("test"));
option b: print(a -> { return a.startsWith("test") });
option c: print(a, b -> a.startsWith("test"));
option d: (a, b) -> {b=1; return b;}
Did you think that option a is the right answer? Awesome! Congrats, you got it correct!

If not, don't worry, you will get there.

  1. option a is a valid lambda with two parameters. Note that curly braces are optional. If braces are not there, return keyword and semicolon is also optional.
  2. option b is wrong and will not compile because it is simply missing a semicolon. Without a semicolon, its not a valid Java statement. As braces are present, we should make sure to add a return keyword and semicolon.
  3. option c is wrong because it doesn't put more than one parameter in a parenthesis.
  4. option d is an invalid lambda. Since it is not allowed to change any parameters' value inside a lambda.

Reference used in this article:

  • Oracle Certified Associate Java SE 8 Programmer | Study Guide
    (by Jeanne Boyarsky and Scott Selikoff)

With this article at OpenGenus, we must have a complete idea of Lambda Expressions in Java. Enjoy.