Java Lambda Expressions - Java Tutorial

Java Lambda Expressions

BLUF: Mastering Java Lambda Expressions is a key requirement for any Java developer. This lesson breaks down the object-oriented principles and syntax required to use this concept in real-world applications.
Write Once, Run Anywhere Tip: Java Lambda Expressions

Java's versatility is unmatched. Learn how Java Lambda Expressions fits into the Java ecosystem and improves your code structure in the tutorial below.

In Java SE 8, a significant addition known as a lambda expression was introduced, offering a concise method to represent a single method interface through an expression. This feature proves particularly valuable within the collection library, aiding in tasks such as iteration, filtering, and data extraction from collections.

The Lambda expression is utilized to supply the implementation of an interface that possesses a functional interface. It helps in reducing the amount of code required. With the lambda expression, there is no necessity to redefine the method for implementing it. Instead, the implementation code is directly written.

In Java, a lambda expression is interpreted as a function, which leads to the omission of generating a .class file by the compiler.

Functional Interface

Lambda expressions offer a way to implement functional interfaces. A functional interface is defined as an interface that contains only a single abstract method. In Java, the @FunctionalInterface annotation is utilized to designate an interface as a functional interface.

Example

@FunctionalInterface

interface MyFunctionalInterface {

    void myMethod(String s);

}

Why use Lambda Expression?

Utilizing lambda expressions in Java enables a more concise and expressive implementation of functional interfaces, reducing verbose boilerplate code and enhancing readability. This updated library introduces a functional programming approach to Java, treating functions as first-class entities. Lambda expressions provide a distinctive capability to process stream collections in a functional manner with a more streamlined syntax.

Their versatility is evident in situations where the functionality needs to be encapsulated and passed as a parameter to enhance modularity and ease of upkeep. Lambda expressions offer a concise substitute for anonymous classes, streamlining the code and decreasing verbosity.

Furthermore, they support the integration of parallel programming structures and provide opportunities to reduce errors by following clean and flawless coding techniques.

Java Lambda Expression Syntax

Example

(argument-list) -> {body}

Java lambda expression consists of the following three components:

  • Argument-list: It can be empty or non-empty as well.
  • Arrow-token: It is used to link the arguments list and body of expression.
  • Body: It contains expressions and statements for lambda expression.

No Parameter Syntax

Example

() -> {

//Body of no parameter lambda

}

One Parameter Syntax

Example

(p1) -> {

//Body of single parameter lambda

}

Two Parameter Syntax

Example

(p1,p2) -> {

//Body of multiple parameter lambda

}

Consider a situation where Java lambda expressions are not utilized. In this case, an interface is implemented without the use of lambda expressions.

Interface Without Lambda Expression

Example

Example

interface Drawable{

	public void draw();

}

public class LambdaExpressionExample {

	public static void main(String[] args) {

		int width=10;



		//without lambda, Drawable implementation using anonymous class

		Drawable d=new Drawable(){

			public void draw(){System.out.println("Drawing "+width);}

		};

		d.draw();

	}

}

Output:

Output

Drawing 10

Java Lambda Expression Example

Next, we will demonstrate the previous example by utilizing Java lambda expressions.

Example

Example

import java.util.Arrays;

import java.util.List;

public class LambdaExample {

    public static void main(String[] args) {

        // Create a list of integers

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

        // Example 1: Using forEach with a lambda expression

        System.out.println("Example 1: Using forEach");

        numbers.forEach(number -> System.out.print(number + " "));

        System.out.println();  // New line for better output formatting

        // Example 2: Using map and reduce to calculate the sum of squares

        System.out.println("Example 2: Using map and reduce");

        int sumOfSquares = numbers.stream()

                                  .map(x -> x * x)

                                  .reduce(0, Integer::sum);

        System.out.println("Sum of squares: " + sumOfSquares);

        // Example 3: Using filter to get even numbers

        System.out.println("Example 3: Using filter");

        List<Integer> evenNumbers = numbers.stream()

                                          .filter(x -> x % 2 == 0)

                                          .toList();  // Convert stream to list (Java 16+)

        System.out.println("Even numbers: " + evenNumbers);

    }

}

Output:

Output

Example 1: Using forEach

1 2 3 4 5 6 7 8 9 10 

Example 2: Using map and reduce

Sum of squares: 385

Example 3: Using filter

Even numbers: [2, 4, 6, 8, 10]

A lambda expression has the flexibility to accept either no arguments or multiple arguments. Let's examine some illustrations:

Java Lambda Expression Example: No Parameter

Example

Example

interface Sayable{

	public String say();

}

public class LambdaExpressionExample3{

public static void main(String[] args) {

	Sayable s=()->{

		return "I have nothing to say.";

	};

	System.out.println(s.say());

}

}

Output:

Output

I have nothing to say.

Java Lambda Expression Example: Single Parameter

Example

Example

interface Sayable{

	public String say(String name);

}



public class LambdaExpressionExample4{

	public static void main(String[] args) {

	

		// Lambda expression with single parameter.

		Sayable s1=(name)->{

			return "Hello, "+name;

		};

		System.out.println(s1.say("Sonoo"));

		

		// You can omit function parentheses  

		Sayable s2= name ->{

			return "Hello, "+name;

		};

		System.out.println(s2.say("Sonoo"));

	}

}

Output:

Output

Hello, Sonoo

Hello, Sonoo

Java Lambda Expression Example: Multiple Parameters

Example

Example

@FunctionalInterface

interface ThreeParametersFunctionalInterface {

    double calculateAverage(double a, double b, double c);

}

public class LambdaExample{

    public static void main(String[] args) {

        // Using lambda expression to implement the calculateAverage method

        ThreeParametersFunctionalInterface myLambda = (a, b, c) -> {

            // Code to be executed with the parameters

            return (a + b + c) / 3.0;

        };

        // Invoking the method defined in the lambda expression

        double average = myLambda.calculateAverage(10.0, 20.0, 30.0);

        System.out.println("Average: " + average);

    }

}

Output:

Output

Average: 20.0

Java Lambda Expression Example: with return keyword

When working with lambda expressions in Java, it is optional to use the return keyword if there is only a single statement. However, it is necessary to include the return keyword when the lambda expression consists of multiple statements.

Example

Example

interface Addable{

	int add(int a,int b);

}



public class LambdaExpressionExample6 {

	public static void main(String[] args) {

		

		// Lambda expression without return keyword.

		Addable ad1=(a,b)->(a+b);

		System.out.println(ad1.add(10,20));

		

		// Lambda expression with return keyword.  

		Addable ad2=(int a,int b)->{

							return (a+b); 

						    };

		System.out.println(ad2.add(100,200));

	}

}

Output:

Output

30

300

Java Lambda Expression Example: without return keyword

Example

Example

// Functional interface with a single abstract method

interface MyFunctionalInterface {

    int add(int a, int b);

}

public class LambdaExample {

    public static void main(String[] args) {

        // Lambda expression without using the return keyword

        MyFunctionalInterface addition = (a, b) -> a + b;

        // Using the lambda expression to perform addition

        int result = addition.add(5, 7);

        // Displaying the result

        System.out.println("Result of addition: " + result);

    }

}

Output:

Output

Result of addition: 12

Java Lambda Expression Example: Foreach Loop

Example

Example

import java.util.*;

public class LambdaExpressionExample7{

	public static void main(String[] args) {

		

		List<String> list=new ArrayList<String>();

		list.add("ankit");

		list.add("mayank");

		list.add("irfan");

		list.add("jai");

		

		list.forEach(

			(n)->System.out.println(n)

		);

	}

}

Output:

Output

ankit

mayank

irfan

jai

Java Lambda Expression Example: Multiple Statements

Example

Example

@FunctionalInterface

interface Sayable{

	String say(String message);

}



public class LambdaExpressionExample8{

	public static void main(String[] args) {

	

		// You can pass multiple statements in lambda expression

		Sayable person = (message)-> {

			String str1 = "I would like to say, ";

			String str2 = str1 + message; 

			return str2;

		};

			System.out.println(person.say("time is precious."));

	}

}

Output:

Output

I would like to say, time is precious.

Java Lambda Expression Example: Creating Thread

Lambda expressions can be utilized to execute threads. The subsequent example demonstrates the implementation of the run method through a lambda expression.

Example

Example

public class LambdaExpressionExample9{

	public static void main(String[] args) {

	

		//Thread Example without lambda

		Runnable r1=new Runnable(){

			public void run(){

				System.out.println("Thread1 is running...");

			}

		};

		Thread t1=new Thread(r1);

		t1.start();

		//Thread Example with lambda

		Runnable r2=()->{

				System.out.println("Thread2 is running...");

		};

		Thread t2=new Thread(r2);

		t2.start();

	}

}

Output:

Output

Thread1 is running...

Thread2 is running...

In Java, lambda expressions are applicable within the collection framework, offering a succinct and effective method to iterate through, filter, and retrieve data. Below are a few instances showcasing lambda expressions and collections.

Java Lambda Expression Example: Comparator

Example

Example

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

class Product{

	int id;

	String name;

	float price;

	public Product(int id, String name, float price) {

		super();

		this.id = id;

		this.name = name;

		this.price = price;

	}

}

public class LambdaExpressionExample10{

	public static void main(String[] args) {

		List<Product> list=new ArrayList<Product>();

		

		//Adding Products

		list.add(new Product(1,"HP Laptop",25000f));

		list.add(new Product(3,"Keyboard",300f));

		list.add(new Product(2,"Dell Mouse",150f));

		

		System.out.println("Sorting on the basis of name...");



		// implementing lambda expression

		Collections.sort(list,(p1,p2)->{

		return p1.name.compareTo(p2.name);

		});

		for(Product p:list){

			System.out.println(p.id+" "+p.name+" "+p.price);

		}



	}

}

Output:

Output

Sorting on the basis of name...

2 Dell Mouse 150.0

1 HP Laptop 25000.0

3 Keyboard 300.0

Java Lambda Expression Example: Filter Collection Data

Example

Example

import java.util.ArrayList;

import java.util.List;

import java.util.stream.Stream; 

class Product{

	int id;

	String name;

	float price;

	public Product(int id, String name, float price) {

		super();

		this.id = id;

		this.name = name;

		this.price = price;

	}

}

public class LambdaExpressionExample11{

	public static void main(String[] args) {

		List<Product> list=new ArrayList<Product>();

		list.add(new Product(1,"Samsung A5",17000f));

		list.add(new Product(3,"Iphone 6S",65000f));

		list.add(new Product(2,"Sony Xperia",25000f));

		list.add(new Product(4,"Nokia Lumia",15000f));

		list.add(new Product(5,"Redmi4 ",26000f));

		list.add(new Product(6,"Lenevo Vibe",19000f));

		

		// using lambda to filter data

		Stream<Product> filtered_data = list.stream().filter(p -> p.price > 20000);

		

		// using lambda to iterate through collection

		filtered_data.forEach(

				product -> System.out.println(product.name+": "+product.price)

		);

	}

}

Output:

Output

Iphone 6S: 65000.0

Sony Xperia: 25000.0

Redmi4 : 26000.0

Java Lambda Expression Example: Event Listener

Example

Example

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JTextField;

public class LambdaEventListenerExample {

	public static void main(String[] args) {

		JTextField tf=new JTextField();

		tf.setBounds(50, 50,150,20);

		JButton b=new JButton("click");

		b.setBounds(80,100,70,30);

		

		// lambda expression implementing here.

		b.addActionListener(e-> {tf.setText("hello swing");});

		

		JFrame f=new JFrame();

		f.add(tf);f.add(b);

		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		f.setLayout(null);

		f.setSize(300, 200);

		f.setVisible(true);



	}



}

Output:

Advantages of Lambda Expressions

  1. Readability and Concision:

Lambda expressions provide a more compact syntax that simplifies the creation of anonymous functions. This results in code that is more readable and easier to maintain, especially when dealing with functional interfaces.

Enhancement for Functional Programming:

Lambda expressions in Java play a significant role in promoting the adoption of functional programming concepts. They enhance code readability and functionality by allowing the utilization of functional interfaces and the incorporation of functional utilities such as reduce, filter, and map functions.

Example

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Without Lambda Expression

names.forEach(new Consumer<String>() {

    @Override

    public void accept(String name) {

        System.out.println(name);

    }

});

// With Lambda Expression

names.forEach(name -> System.out.println(name));
  1. Parallelism and Concurrency:

Lambda expressions are particularly useful when dealing with these two concepts as they simplify performing parallel operations on collections by leveraging the Streams API.

Example

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

// Without Lambda Expression

int sumWithoutLambda = numbers.parallelStream().reduce(0, new BinaryOperator<Integer>() {

    @Override

    public Integer apply(Integer x, Integer y) {

        return x + y;

    }

});

// With Lambda Expression

int sumWithLambda = numbers.parallelStream().reduce(0, (x, y) -> x + y);
  1. Behavior Encapsulation:

Lambda expressions offer a compact and versatile method to depict behavior, enhancing the flexibility and modularity of programming. They allow behavior to be passed as an argument to methods, facilitating portability.

Example

// Without Lambda Expression

List<String> namesUpperCase = transformList(names, new StringTransformer() {

    @Override

    public String transform(String input) {

        return input.toUpperCase();

    }

});

// With Lambda Expression

List<String> namesUpperCaseLambda = transformList(names, input -> input.toUpperCase());
  1. Better Collection Iteration:

Lambda expressions enhance code readability by eliminating redundant code and streamlining iteration over collections.

Example

List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");

// Without Lambda Expression

for (String fruit : fruits) {

    System.out.println(fruit);

}

// With Lambda Expression

fruits.forEach(fruit -> System.out.println(fruit));

Disadvantages of lambda expressions

  1. Limited to Functional Interfaces:

Lambda expressions are only compatible with functional interfaces, which are interfaces that have only one abstract method. Due to this restriction, lambda expressions can only be used in certain scenarios. This can pose a challenge when working with interfaces that have more than one abstract method.

Example

// Example of an interface with more than one abstract method

interface MultiAbstractMethodInterface {

    void method1();

    void method2();

}

// Lambda expression cannot be used with MultiAbstractMethodInterface

// as it violates the "Functional Interface" requirement
  1. Challenges with Comprehension when Dealing with Sophisticated Logic:

Lambda functions are beneficial for concise and simple operations. However, their readability may decrease when dealing with more complex logic. In such cases, opting for an alternative class or a traditional method could be more appropriate.

Example

// Lambda expression with complex logic

Function<Integer, Integer> complexLambda = x -> {

    // Complex logic here

    return x * x + 2 * x + 1;

};
  1. Debugging:

Debugging lambda expressions can pose challenges, especially when they are complex or involve multiple layers of abstraction. Stack traces may not provide precise information about the specific issue within a lambda expression.

Challenges with State Management:

Lambda expressions are characterized by their statelessness, lacking any internal state. This feature can make it challenging to manage mutable states and to capture variables from outer scopes, especially in scenarios involving concurrent or parallel streams.

Example

// Managing state in lambda expressions

AtomicInteger count = new AtomicInteger(0);

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

numbers.forEach(n -> {

    count.incrementAndGet(); // Manipulating shared mutable state within lambda expressions

});
  1. Performance Impact in Specific Scenarios:

While lambda expressions are tailored for contemporary Java runtime environments, there are scenarios where their usage can lead to a slight performance cost. This might be insignificant for most applications, but it should be considered in performance-critical contexts.

Example

// Example of a performance overhead (negligible in most cases)

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



// Without Lambda Expression

int sumWithoutLambda = numbers.parallelStream().reduce(0, new BinaryOperator<Integer>() {

    @Override

    public Integer apply(Integer x, Integer y) {

        return x + y;

    }

});



// With Lambda Expression

int sumWithLambda = numbers.parallelStream().reduce(0, (x, y) -> x + y);

Java Lambda Expressions MCQ

  1. What is a key feature of lambda expressions in Java?
  • They are primarily used for exception handling.
  • They enable functional programming by allowing functions to be passed as arguments.
  • They are used for object serialization.
  • They replace all instances of anonymous inner classes.

Explanation: Lambda expressions enable functional programming in Java by allowing functions to be passed as arguments, making code more concise and readable.

  1. Which interface must a lambda expression implement?
  • Serializable
  • Runnable
  • FunctionalInterface
  • Cloneable

Explanation: A lambda expression must implement a functional interface, which is an interface with a single abstract method.

  1. How would you represent the lambda expression (int x, int y) -> x + y in Java?
  • As a method reference
  • As an anonymous class
  • As a functional interface implementation
  • As a lambda expression

Explanation: The expression (int x, int y) -> x + y is a lambda expression that takes two integer arguments and returns their sum.

  1. Which Java package contains the Function interface used with lambda expressions?
  • java.lang
  • java.util
  • java.util.function
  • java.io

Explanation: The Function interface, along with other functional interfaces used with lambda expressions, is contained in the java.util.function package.

  1. What does the following lambda expression do: -> System.out.println("Hello")?
  • It defines a Runnable interface.
  • It creates a thread.
  • It prints "Hello" when invoked.
  • It returns a string "Hello".

The lambda syntax -> System.out.println("Hello") represents a lambda expression without any arguments. When this expression is called, it will output "Hello" to the console.

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience