Java 8 Features — Lambda Expressions

Complete Training Notes

Prepared by Srikanth Mamillapalli

1. What is a Lambda Expression?

Lambda Expressions are a Java 8 language feature that allows us to consider actions as objects.

In Java, lambda expressions basically express instances of functional interfaces. An interface with a single abstract method is called a functional interface.

Lambda expressions are short blocks of code that accept input as parameters and return a resultant value.

Main Objective: The main objective of Lambda Expressions is to bring the benefits of functional programming into Java.

Definition

A Lambda Expression is an anonymous (nameless) function. It does not have a name, return type, or access modifier.

Lambda expressions are also known as anonymous functions or closures.

2. Why Use Lambda Expressions?

  • To provide the implementation of a Functional Interface.
  • Less coding.
  • Enable functionality to be treated as a method argument, or code as data.
  • A function can be created without belonging directly to a named class.
  • A lambda expression can be passed around like an object and executed on demand.
Lambda expressions implement the single abstract function of a functional interface. Lambda expressions were added in Java 8.

3. Parts of a Lambda Expression

Argument List
Arrow Token
->
Body
PartDescription
Argument ListCan be empty or non-empty.
Arrow TokenLinks the argument list and the body of the expression.
BodyContains expressions and statements for the lambda expression.
(Argument List) -> { expression; }

4. How to Write a Lambda Expression

Consider a traditional method for adding two numbers and printing the result:

private void add(int i, int j) {
    System.out.println(i + j);
}
Step 1 — Remove the access modifier
void add(int i, int j) {
    System.out.println(i + j);
}
Step 2 — Remove the return type
add(int i, int j) {
    System.out.println(i + j);
}
Step 3 — Remove the method name
(int i, int j) {
    System.out.println(i + j);
}
Step 4 — Insert the arrow sign
(int i, int j) -> {
    System.out.println(i + j);
}
Step 5 — Remove parameter types when the compiler can infer them
(i, j) -> {
    System.out.println(i + j);
}
Final Lambda Expression:
(i, j) -> {
    System.out.println(i + j);
}

5. Lambda Expression Examples

Example 1 — Print "Hello Java 8"

public void m1() {
    System.out.println("Hello Java 8");
}

// Lambda form
() -> {
    System.out.println("Hello Java 8");
}

Example 2 — Multiply Two Numbers

public void add(int a, int b) {
    System.out.println(a * b);
}

// Remove method declaration elements
(int a, int b) -> System.out.println(a * b);

// Type inference
(a, b) -> System.out.println(a * b);

Example 3 — String Operation

public String m2(String str1) {
    return str2;
}

// Lambda form
(String str1) -> return str2;

// Simplified
(str1) -> str2;
The source presents this String example as a transformation example. The exact variable naming in the original example is retained here.

6. Lambda Expression Syntax

Lambda Operator
Body
lambda operator -> body
(Argument List) -> {expression;}

Example with Two Arguments

(int arg1, String arg2) -> {
    System.out.println(
        "Two arguments " + arg1 + " and " + arg2
    );
}
ComponentExample
Argument List(int arg1, String arg2)
Arrow Token->
Body{ System.out.println(...); }

7. Lambda Expression Parameters

The notes describe three forms:

1. Zero Parameter

No arguments.

2. Single Parameter

One argument.

3. Multiple Parameters

Two or more arguments.

Zero Parameter

() -> System.out.println("Zero parameter lambda");

Single Parameter

(p) -> System.out.println("One parameter: " + p);

It is not mandatory to use parentheses when the parameter type can be inferred from the context.

Multiple Parameters

(p1, p2) -> System.out.println(
    "Multiple parameters: " + p1 + ", " + p2
);
Important points:
  • The body can contain zero, one, or more statements.
  • For a single statement, curly braces are not mandatory and the return type of the anonymous function is the same as the body expression.
  • For multiple statements, curly braces are required and the return type is the same as the value returned within the code block, or void if nothing is returned.

8. Lambda with ArrayList and forEach()

// {1, 2, 3, 4}
ArrayList<Integer> arrL = new ArrayList<Integer>();

arrL.add(1);
arrL.add(2);
arrL.add(3);
arrL.add(4);

// Using lambda expression to print all elements
arrL.forEach(n -> System.out.println(n));

// Using lambda expression to print even elements
arrL.forEach(n -> {
    if (n % 2 == 0)
        System.out.println(n);
});
Lambda expressions can only be used to implement functional interfaces. In the example, the lambda expression implements the Consumer functional interface.

9. Functional Interface Example

// Java program to demonstrate lambda expressions
// to implement a user defined functional interface.

// A sample functional interface
// An interface with single abstract method

interface FuncInterface {

    // An abstract function
    void abstractFun(int x);

    // A non-abstract (default) function
    default void normalFun() {
        System.out.println("Hello");
    }
}

Using the Functional Interface

class Test {
    public static void main(String args[]) {

        // Lambda expression to implement
        // above functional interface

        FuncInterface fobj =
            (int x) -> System.out.println(2 * x);

        // Calls lambda expression and prints 10
        fobj.abstractFun(5);
    }
}

10. Lambda with Multiple Functional Behaviors

public class Test {

    interface FuncInter1 {
        int operation(int a, int b);
    }

    interface FuncInter2 {
        void sayMessage(String message);
    }

    // Performs FuncInter1's operation
    private int operate(int a, int b, FuncInter1 fobj) {
        return fobj.operation(a, b);
    }

    public static void main(String args[]) {

        // Lambda for addition
        FuncInter1 add =
            (int x, int y) -> x + y;

        // Lambda for multiplication
        FuncInter1 multiply =
            (int x, int y) -> x * y;

        Test tobj = new Test();

        System.out.println(
            "Addition is " + tobj.operate(6, 3, add)
        );

        System.out.println(
            "Multiplication is " + tobj.operate(6, 3, multiply)
        );

        // Lambda for single parameter
        FuncInter2 fobj = message ->
            System.out.println("Hello " + message);

        fobj.sayMessage("Geek");
    }
}

11. Anonymous Inner Class vs Lambda Expression

Anonymous Inner Class Lambda Expression
It is a class without a name. It is a method/function without a name (anonymous function).
Can extend abstract and concrete classes. Cannot extend abstract or concrete classes.
Can implement an interface containing any number of abstract methods. Can implement an interface containing a single abstract method (Functional Interface).
Can declare instance variables. Cannot declare instance variables; declared variables act as local variables according to the notes.
Can be instantiated. Cannot be instantiated.
this refers to the current anonymous inner-class object. this refers to the current outer/enclosing class object.
Best choice when multiple methods need to be handled. Best choice when handling an interface with a single abstract method.

12. Compilation and Memory Notes

Anonymous Inner ClassLambda Expression
At compilation time, a separate .class file is generated, such as OuterClass$1.class. The notes state that no separate dot-class file is generated for a lambda expression and describe it as being converted into a private method of the outer class.
Memory is allocated on demand whenever an object is created. The notes describe it as residing in the permanent memory of the JVM / Method Area.
The table above preserves the explanation from the source material.

13. Zero Parameter Lambda — Complete Example

@FunctionalInterface
interface GreetingService {
    void greet();
}

public class LambdaExampleNoParameter {

    public static void main(String[] args) {

        GreetingService service =
            () -> System.out.println("Hello, World!");

        service.greet();
    }
}

14. Single Parameter Lambda — Complete Example

@FunctionalInterface
interface Printer {
    void print(String message);
}

public class LambdaExampleSingleParameter {

    public static void main(String[] args) {

        Printer printer =
            msg -> System.out.println("Message: " + msg);

        printer.print("Lambda in Java 8");
    }
}

Runnable with Lambda

public class LambdaExpressionExample {

    public static void main(String[] args) {

        // using Lambda Expression
        new Thread(() ->
            System.out.println(
                "Thread is started: using Lambda Expressions"
            )).start();

        // old way
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(
                    "Thread is started: using old method"
                );
            }
        }).start();
    }
}

15. Multiple Parameter Lambda — Complete Example

@FunctionalInterface
interface Calculator {
    int operate(int a, int b);
}

public class LambdaMultipleParams {

    public static void main(String[] args) {

        Calculator add =
            (a, b) -> a + b;

        Calculator subtract =
            (a, b) -> a - b;

        Calculator multiply =
            (a, b) -> a * b;

        Calculator divide =
            (a, b) -> b != 0 ? a / b : 0;

        Calculator mod =
            (a, b) -> a % b;

        System.out.println(
            "Addition: " + add.operate(10, 5)
        );

        System.out.println(
            "Subtraction: " + subtract.operate(10, 5)
        );

        System.out.println(
            "Multiplication: " + multiply.operate(10, 5)
        );

        System.out.println(
            "Division: " + divide.operate(10, 5)
        );

        System.out.println(
            "Mod: " + mod.operate(10, 5)
        );
    }
}

16. Lambda Expression with Return Value

@FunctionalInterface
interface Adder {
    int add(int a, int b);
}

public class LambdaExampleWithReturnValue {

    public static void main(String[] args) {

        Adder adder =
            (a, b) -> a + b;

        System.out.println(
            "Sum: " + adder.add(10, 20)
        );
    }
}
When the lambda body is a single expression such as (a, b) -> a + b, the expression value acts as the return value.

17. Lambda with Collections

import java.util.Arrays;

public class LambdaExampleWithCollection {

    public static void main(String[] args) {

        List<String> names =
            Arrays.asList(
                "Sree",
                "Vihas",
                "Bhargav",
                "Rohith"
            );

        // Sorting using lambda
        Collections.sort(
            names,
            (s1, s2) -> s1.compareTo(s2)
        );

        names.forEach(
            name -> System.out.println(name)
        );
    }
}
The source demonstrates sorting with a lambda comparator and iterating through the collection using forEach().

18. Lambda with Runnable

public class LambdaRunnable {

    public static void main(String[] args) {

        Runnable r = () -> {

            for (int i = 0; i < 5; i++) {
                System.out.println(
                    "Running in thread " + i
                );
            }

        };

        new Thread(r).start();
    }
}

19. Lambda with Callable and ExecutorService

public class LambdaCallableExample {

    public static void main(String[] args)
            throws Exception {

        ExecutorService executor =
            Executors.newSingleThreadExecutor();

        // Lambda expression implementing Callable
        Callable<String> callableTask = () -> {

            Thread.sleep(1000);

            return "Task completed using Callable with Lambda!";
        };

        // Submit the task to executor
        Future<String> future =
            executor.submit(callableTask);

        // Get the result of the callable
        String result = future.get();

        // This will wait until task completes
        System.out.println(result);

        // Shutdown the executor
        executor.shutdown();
    }
}

20. Quick Revision

TopicKey Point
Lambda ExpressionAnonymous function introduced in Java 8.
Functional InterfaceInterface with a single abstract method.
Main PurposeProvide functional-programming benefits and reduce coding.
Syntax(arguments) -> body
Zero Parameter() -> expression
Single Parameterp -> expression or (p) -> expression
Multiple Parameters(p1, p2) -> expression
BodyCan contain one or more statements.
forEachLambda can be used to process collection elements.
RunnableLambda can provide the Runnable implementation.
CallableLambda can provide a Callable implementation returning a value.
Anonymous ClassClass without a name; can implement interfaces with multiple abstract methods.
LambdaTargets a functional interface with a single abstract method.
thisLambda's this refers to the enclosing class according to the notes.
Java 8
Functional Interface
Lambda Expression
Less Code + Functional Programming