Java 8 Lambda Expressions

In this post, we will discuss the most important feature of Java 8 which is Lambda Expressions. We will learn Lambda Expressions with lots of examples.

1. What Are Lambda Expressions? 

A lambda expression is simply a function without any name. It can even be used as a parameter in a function. Lambda Expression facilitates functional programming and simplifies development a lot.

The main use of Lambda expression is to provide an implementation for functional interfaces.

Syntax of Lambda Expression 

(parameters) -> expression
Or
(parameters) -> { statements; }
The syntax of a lambda expression is characterized by the following three parts: 
Parameters: A lambda expression can have zero or more parameters, enclosed in parentheses. 

Arrow Token: The arrow token -> separates the parameters from the body of the lambda. 

Body: The body of the lambda contains expressions or statements describing the method's functionality.

Here's a simple textual representation:
  +------------+    +----+    +-----------------------+
  | Parameters | -> | -> | -> | Body (expression/code) |
  +------------+    +----+    +-----------------------+

Lambda Expression Syntax Examples

Introduction to Functional Interface

Lambda expression provides an implementation of the Java 8 Functional Interface. An interface that has only one abstract method is called a functional interface. 
Java provides an annotation @FunctionalInterface, which is used to declare an interface as a functional interface.
If you have used Runnable, Callable, Comparator, FileFilter, PathMatcher, and EventHandler interfaces in your projects then you can replace its implementation with Lambda Expression.
Learn Functional Interface in detail - Java 8 Functional Interface.

2. Benefits of Using Lambda Expressions 

Conciseness: They allow for a more concise way of expressing instances of single-method interfaces (functional interfaces). 
Functional Programming: They enable functional programming styles in Java. 
Improved Readability: They often make the code more readable, especially when using functional interfaces. 
Flexibility: They can be used wherever a functional interface is expected. 
Interoperability with Streams: They work seamlessly with the Stream API, allowing for powerful and expressive data processing.

3. Use Cases 

Lambda expressions are often used with functional interfaces in the java.util.function package, such as Predicate, Function, Consumer, and Supplier. They're also commonly used with the Streams API for operations like filtering, mapping, and reducing.

4. Java Lambda Expressions Examples

1. Java Simple Lambda Expression Example

Here, we are implementing a Drawable functional interface method using the lambda expression:
interface Drawable{  
    public void draw();  
}  
public class LambdaExpressionExample {  
    public static void main(String[] args) {  
        int width=10;         
        //with lambda 
        Drawable withLambda=()->{  
            System.out.println("Drawing "+width);  
        };  
        withLambda.draw();  
    }  
} 
Output :
Drawing 10

2. Java Lambda Expression Example: No Parameter

If the abstract method takes no parameters and returns no value, the lambda expression can be written with empty parentheses and a body. Please refer to the comments in the code, which indicates that code with Lambda expression and without Lambda expression.

interface Sayable {
    public String say();
}
public class JLEExampleNoParameter {
    public static void main(String[] args) {
        // without lambda expression
        Sayable sayable = new Sayable() {
            @Override
            public String say() {
                return "Return something ..";
            }
        };
        sayable.say();

        // with lambda expression
        Sayable withLambda = () -> "Return something ..";
        withLambda.say();
    }
}

3. Java Lambda Expression Example: Single Parameter

If the abstract method takes a single parameter, you can omit the parentheses around the parameter:

interface Printable {
    void print(String msg);
}

public class JLEExampleSingleParameter {

    public static void main(String[] args) {
         // with lambda expression
         Printable withLambda = msg -> System.out.println(msg);
         withLambda.print(" Print message to console....");
     }
}
Output :
 Print message to console....

4. Java Lambda Expression Example: Multiple Parameters

If the abstract method takes multiple parameters, you must include parentheses around the parameters:

interface Addable{  
    int add(int a,int b);  
}  
public class JLEExampleMultipleParameters {

    public static void main(String[] args) {
       // Multiple parameters in lambda expression  
        Addable withLambda = (a,b)->(a+b);  
        System.out.println(withLambda.add(10,20));  
          
        // Multiple parameters with data type in lambda expression  
        Addable withLambdaD = (int a,int b) -> (a+b);  
        System.out.println(withLambdaD.add(100,200));  
    }
 }

5. Java Lambda Expression Example: Multiple Statements

If the body consists of multiple statements, you must include braces and use a return statement:

interface IAvarage{  
    double avg(int[] array);  
}  
public class JLEExampleMultipleStatements {

    public static void main(String[] args) {

        IAvarage withLambda = (withLambdaArray) -> {
            double sum = 0;
            int arraySize = withLambdaArray.length;
   
            System.out.println("arraySize : " + arraySize);
            for (int i = 0; i < withLambdaArray.length; i++) {
                sum = sum + withLambdaArray[i]; 
            }
            System.out.println("sum : " + sum);
   
            return (sum/ arraySize);
       };
  
        int[] withLambdaArray = {1,4,6,8,9};
        System.out.println(withLambda.avg(withLambdaArray)); 
    }
}

6. Java Lambda Expression Example: Creating Thread

Here is an example of how to use a lambda expression to replace an anonymous class for implementing the Runnable interface in Java.

public class JLEExampleRunnable {

    public static void main(String[] args) {
  
    //without lambda, Runnable implementation using anonymous class  
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println(" Runnable example without lambda exp.");
        }
    };
    Thread thread = new Thread(runnable);
    thread.start();
  
     //with lambda 
    Runnable withLambda = () -> System.out.println(" Runnable example with lambda exp.");
    Thread thread1 = new Thread(withLambda);
    thread1.start();
  }
}
Output :
 Runnable example without lambda exp.
 Runnable example with lambda exp.

Without Lambda: The first part of the code creates a Runnable object using an anonymous class. This object is passed to a Thread, which is then started. When the thread executes, it prints "Runnable example without lambda exp." to the console.

With Lambda: The second part of the code shows how to achieve the same functionality using a lambda expression. Instead of using an anonymous class, a lambda expression is assigned to a Runnable variable, withLambda. This lambda expression has the same functionality as the anonymous class but is much more concise. A new thread is created with this lambda expression and started, printing "Runnable example with lambda exp." to the console.

7. Java Lambda Expression Example: Comparator

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class JLEComparatorExample {

    public static void main(String[] args) {

        List<Person> listOfPerson = new ArrayList<Person>();
        listOfPerson.add(new Person("abc", 27));
        listOfPerson.add(new Person("mno", 26));
        listOfPerson.add(new Person("pqr", 28));
        listOfPerson.add(new Person("xyz", 27));

        // Without lambda expression.
        // Sort list by age
        Comparator<Person> comparator = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        };

        Collections.sort(listOfPerson, comparator);

        System.out.println(" sort persons by age in ascending order");
        for (Person person : listOfPerson) {
            System.out.println(" Person name : " + person.getName());
        }

        // Witht lambda expression.
        // Sort list by age

        Collections.sort(listOfPerson, (Person o1, Person o2) -> {
            return o1.getAge() - o2.getAge();
        });
        // Use forEach method added in java 8
        System.out.println(" sort persons by age in ascending order");
        listOfPerson.forEach(
           (person) -> System.out.println(" Person name : " + person.getName()));
        }
  }

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
       super();
       this.name = name;
       this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

8. Iterate ArrayList with forEach() method + Lambda Expression Example

package net.javaguides.collections;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * 
 * Java program to demonstrate different ways to Iterate over an ArrayList in Java
 * @author Ramesh Fadatare
 *
 */
public class DifferentWaysListIterateProgram {

    public static void main(String...args) {

        List < String > courses = Arrays.asList("C", "C++", "Core Java", "J2EE", "Spring", "Hibernate", "Python");

   
        // JDK 8 streaming example lambda expression
        courses.stream().forEach(course -> printCourse(course));

        // JDK 8 streaming example method reference
        courses.stream().forEach(DifferentWaysListIterateProgram::printCourse);

        // JDK 8 for each with lambda
        courses.forEach(course -> printCourse(course));

        // JDK 8 for each
        courses.forEach(DifferentWaysListIterateProgram::printCourse);
    }

    // common method to print course
    private static void printCourse(String course) {
        System.out.println("course name :: " + course);
    }
}

9. Iterate HashSet with forEach() method + Lambda Expression Example

package net.javaguides.collections;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Java program to demonstrate different ways to iterate over a Set in Java
 * 
 * @author Ramesh Fadatare
 *
 */
public class DifferentWaysSetIterateProgram {

    public static void main(String...args) {

        Set < String > courses = new HashSet < String > ();
        courses.add("Java");
        courses.add("C");
        courses.add("C++");
        courses.add("Python");
        courses.add("Scala");

        // JDK 8 streaming example lambda expression
        courses.stream().forEach(course -> coursePrinter(course));

        // JDK 8 streaming example method reference
        courses.stream().forEach(DifferentWaysSetIterateProgram::coursePrinter);

        // JDK 8 for each with lambda
        courses.forEach(course -> coursePrinter(course));

        // JDK 8 for each
        courses.forEach(DifferentWaysSetIterateProgram::coursePrinter);
    }

    // common method to print course
    private static void coursePrinter(String course) {
        System.out.println("course name :: " + course);
    }
}

10. Iterate HashMap with forEach() method + Lambda Expression Example

package net.javaguides.collections;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * This program demonstrate different ways to iterate over a Map in Java
 * 
 * @author Ramesh Fadatare
 *
 */
public class DifferentWaysMapIterateProgram {

    public static void main(String...args) {

        Map < Integer, String > coursesMap = new HashMap < Integer, String > ();
        coursesMap.put(1, "C");
        coursesMap.put(2, "C++");
        coursesMap.put(3, "Java");
        coursesMap.put(4, "J2EE");
        coursesMap.put(5, "Python");
        coursesMap.put(6, "Scala");

   
        // JDK 8 for each with lambda
        coursesMap.forEach((k, v) -> coursePrinter(k, v));

        // JDK 8 for each method reference
        coursesMap.forEach(DifferentWaysMapIterateProgram::coursePrinter);

    }

    // common method to print map key value
    private static void coursePrinter(Integer number, String brand) {
        System.out.println("course no : " + number + " and course name : " + brand);
    }
}

Summary - key points about lambda expressions

Syntax Simplicity: The syntax of a lambda expression is much more concise than using anonymous inner classes. 

Functional Interfaces: They are used primarily to define the implementation of an abstract method defined in a functional interface (an interface with exactly one abstract method). 

Parameters and Body: The structure of a lambda consists of a set of parameters, an arrow ->, and the body of the lambda. 

Type Inference: Java's type inference mechanism can often determine the type of the parameters, allowing you to skip declaring them. 

Return Type: If the body of the lambda consists of a single expression, the return type will be inferred, and the return keyword is not needed. 

No Access Modifiers: Lambda expressions do not have access modifiers or a throws clause. 

Local Variable Access: Lambdas can access final or effectively final local variables of the enclosing scope. 

Parallel Execution Support: They can be used to facilitate parallel processing, like in streams. 

Enhanced Iteration: They can be used with new iteration methods provided by the Iterable interface to make iterations over collections more concise. 

Functional Programming Paradigm: Lambda expressions bring elements of functional programming into Java and enable functional programming techniques and styles. 

Immutable: The variables used inside lambda expressions must be final or effectively final, making them immutable. 

Interoperability with SAM Interfaces: Lambda expressions can be used wherever a Single Abstract Method (SAM) interface is expected. 

Target Typing: The target type of a lambda expression is determined by the context in which the lambda is used. 

Performance: Lambda expressions are often more performant than anonymous classes since they are not compiled into separate classes. 

Functional Method References: They can be used in combination with method references to further simplify code where a method already exists to perform an operation. 

No, this Reference: Inside a lambda expression, this keyword refers to the enclosing instance, not the lambda itself.

Source code on GitHub

The source code of this post is available on GitHub Repository.

4. Related Java 8 Top Posts

  1. Java 8 Functional Interfaces
  2. Java 8 Method References
  3. Java 8 Stream API
  4. Java 8 Optional Class
  5. Java 8 Collectors Class
  6. Java 8 StringJoiner Class
  7. Java 8 Static and Default Methods in Interface
  8. Guide to Java 8 forEach Method
  9. Handle NullPointerException in Controller, Service, and DAO Layer using Java 8 Optional Class
  10. How to Use Java 8 Stream API in Java Projects
  11. Migrating Source Code to Java 8

Comments

  1. Hi Ramesh , Thank you for the examples. could you please check 3.8 with Lamda example? I think runnable need to be passed to thread.

    ReplyDelete
    Replies
    1. Hey Udaykiran you are right for with lambda example i missed to create thread object and pass runnable object to it. Good catch thank you.

      Delete
    2. Download source code from my GitHub Repo of all Java 8 tutorials (lambda, streams, functional interface etc) on https://github.com/RameshMF/java8-developer-guide. Hope this helps you.

      Delete

Post a Comment

Leave Comment