Java Lambda Expressions: Techniques for Advanced Developers

Java Lambda Expressions

Marcelo Domingues
7 min readJun 19, 2024
Reference Image

Introduction

Lambda expressions, introduced in Java 8, have revolutionized the way developers write code by enabling functional programming capabilities in Java. They allow for more concise and expressive code, enabling developers to treat functionality as a method argument or a piece of data. This medium article will explore the fundamentals of lambda expressions, compare them to traditional methods, cover the various types of lambda expressions, and demonstrate their use with detailed examples and best practices.

What are Lambda Expressions?

Lambda expressions are anonymous functions that provide a clear and concise way to represent one method interface using an expression. They enable developers to write more functional-style code, which is often more readable and less error-prone.

Syntax of Lambda Expressions

The syntax of a lambda expression is as follows:

(parameters) -> expression

or

(parameters) -> { statements; }
  • Parameters: A comma-separated list of formal parameters enclosed in parentheses. If there is only one parameter, the parentheses can be omitted.
  • Arrow Token: The arrow token (->) separates the parameters from the body of the lambda expression.
  • Body: The body can be a single expression or a block of statements. If the body is a single expression, the return keyword and curly braces can be omitted.

Comparing Traditional Methods and Lambda Expressions

Traditional Approach

Before lambda expressions, implementing functional interfaces required creating anonymous inner classes. This approach often led to verbose and less readable code.

Example: Sorting a List Using an Anonymous Inner Class

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

public class TraditionalExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});

for (String name : names) {
System.out.println(name);
}
}
}

Lambda Expression Approach

Lambda expressions simplify the process, making the code more concise and readable.

Example: Sorting a List Using a Lambda Expression

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

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

names.sort((a, b) -> a.compareTo(b));

names.forEach(System.out::println);
}
}

Types of Lambda Expressions

Lambda expressions can be categorized based on their parameter lists and bodies.

No Parameters

A lambda expression with no parameters uses empty parentheses.

Example:

() -> System.out.println("Hello, World!")

Usage:

@FunctionalInterface
interface Greeting {
void sayHello();
}

public class LambdaExample {
public static void main(String[] args) {
Greeting greeting = () -> System.out.println("Hello, World!");
greeting.sayHello();
}
}

One Parameter

A lambda expression with one parameter can omit the parentheses around the parameter.

Example:

str -> System.out.println(str)

Usage:

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

public class LambdaExample {
public static void main(String[] args) {
Printer printer = str -> System.out.println(str);
printer.print("Hello, Lambda!");
}
}

Multiple Parameters

A lambda expression with multiple parameters requires parentheses around the parameter list.

Example:

(a, b) -> a + b

Usage:

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

public class LambdaExample {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
System.out.println("Sum: " + addition.operate(5, 3));
}
}

Multiple Statements

A lambda expression with multiple statements requires curly braces and a return statement if the lambda returns a value.

Example:

(a, b) -> {
int result = a + b;
return result;
}

Usage:

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

public class LambdaExample {
public static void main(String[] args) {
MathOperation addition = (a, b) -> {
int result = a + b;
return result;
};
System.out.println("Sum: " + addition.operate(5, 3));
}
}

Using Lambda Expressions with Collections

Iterating Over a Collection

Lambda expressions can be used to iterate over collections using the forEach method.

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

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

names.forEach(name -> System.out.println(name));
}
}

Filtering a Collection

Lambda expressions can be used to filter collections using the stream API.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("J"))
.collect(Collectors.toList());

filteredNames.forEach(System.out::println);
}
}

Sorting a Collection

Lambda expressions can be used to sort collections using the Comparator interface.

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

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

names.sort((a, b) -> a.compareTo(b));

names.forEach(System.out::println);
}
}

Functional Interfaces

Lambda expressions work with functional interfaces. A functional interface is an interface with a single abstract method. Java provides several built-in functional interfaces in the java.util.function package, including Predicate, Function, Consumer, and Supplier.

Predicate

The Predicate interface represents a boolean-valued function of one argument.

Example:

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

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

Predicate<String> startsWithJ = name -> name.startsWith("J");

names.stream()
.filter(startsWithJ)
.forEach(System.out::println);
}
}

Function

The Function interface represents a function that takes one argument and produces a result.

Example:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

Function<String, Integer> nameLength = name -> name.length();

List<Integer> nameLengths = names.stream()
.map(nameLength)
.collect(Collectors.toList());

nameLengths.forEach(System.out::println);
}
}

Consumer

The Consumer interface represents an operation that takes a single input argument and returns no result.

Example:

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

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

Consumer<String> printName = name -> System.out.println(name);

names.forEach(printName);
}
}

Supplier

The Supplier interface represents a supplier of results.

Example:

import java.util.function.Supplier;

public class LambdaExample {
public static void main(String[] args) {
Supplier<String> supplier = () -> "Hello, World!";
System.out.println(supplier.get());
}
}

Benefits of Using Lambda Expressions

  • Conciseness: Lambda expressions allow you to write more concise code by removing the need for boilerplate code such as anonymous classes.
  • Readability: Lambda expressions make the code more readable by expressing the intent of the code more clearly and succinctly.
  • Flexibility: Lambda expressions provide greater flexibility in how functionality is defined and passed around in the code. They enable functional programming techniques that can lead to more modular and reusable code.
  • Parallel Processing: Lambda expressions, when used with the stream API, enable parallel processing of collections, which can lead to significant performance improvements.

Best Practices for Using Lambda Expressions

Use Descriptive Variable Names

When writing lambda expressions, use descriptive variable names to make the code more readable.

names.forEach(name -> System.out.println(name));

Keep Lambda Expressions Short

Keep lambda expressions short and concise. If the lambda expression is too long, consider extracting it into a separate method.

names.stream()
.filter(name -> name.startsWith("J"))
.forEach(System.out::println);

Use Method References When Possible

When a lambda expression merely calls an existing method, use a method reference instead.

names.forEach(System.out::println);

Handle Exceptions Appropriately

Handle exceptions within lambda expressions appropriately. If a lambda expression throws a checked exception, it must be handled within the lambda expression.

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

public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");

names.forEach(name -> {
try {
System.out.println(name);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}

Conclusion

Lambda expressions are a powerful feature in Java that enable functional programming techniques and make the code more concise, readable, and maintainable. By understanding the syntax and usage of lambda expressions, you can leverage their full potential in your Java applications. Whether you are iterating over collections, filtering data, or sorting lists, lambda expressions provide a flexible and efficient way to handle these tasks. By following best practices, you can write clean, modular, and reusable code that takes advantage of the benefits offered by lambda expressions.

References:

  1. Java™ Platform, Standard Edition 8 API Specification. Retrieved from https://docs.oracle.com/javase/8/docs/api/
  2. Bloch, J. (2018). Effective Java (3rd Edition). Addison-Wesley Professional.
  3. Oracle. (2014). Java SE 8 for the Really Impatient. Retrieved from https://www.oreilly.com/library/view/java-se-8/9780133430207/
  4. Raoul-Gabriel Urma, Mario Fusco, and Alan Mycroft. (2014). Java 8 in Action: Lambdas, Streams, and Functional-style Programming. Manning Publications.
  5. Venkat Subramaniam. (2014). Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions. Pragmatic Bookshelf.
  6. Brian Goetz. (2014). State of the Lambda: Libraries Edition. Retrieved from https://cr.openjdk.java.net/~briangoetz/lambda/lambda-libraries-final.html
  7. Oracle. (2014). Java Tutorials: Lambda Expressions. Retrieved from https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
  8. Java Community Process. (2013). JSR 335: Lambda Expressions for the Java Programming Language. Retrieved from https://jcp.org/en/jsr/detail?id=335

Explore More on Spring and Java Development:

Enhance your skills with our selection of articles:

  • Mastering Spring Security: Roles and Privileges (Jun 19, 2024): Learn the essentials of managing roles and privileges in Spring Security. Read More
  • Publishing Your Java Library to Maven Central: A Step-by-Step Tutorial (Mar 25, 2024): A comprehensive guide to making your Java library accessible to millions of developers worldwide. Read More
  • The Art of Unit Testing: Elevate Your Code with Effective Mocks (Mar 13, 2024): A complete guide to using mocks in unit testing, emphasizing the benefits of component isolation. Read More
  • Spring Beans Mastery (Dec 17, 2023): Unlock advanced application development techniques. Read More
  • JSON to Java Mapping (Dec 17, 2023): Streamline your data processing. Read More
  • Spring Rest Tools Deep Dive (Nov 15, 2023): Master client-side RESTful integration. Read More
  • Dependency Injection Insights (Nov 14, 2023): Forge better, maintainable code. Read More
  • Spring Security Migration (Sep 9, 2023): Secure your upgrade smoothly. Read More
  • Lambda DSL in Spring Security (Sep 9, 2023): Tighten security with elegance. Read More
  • Spring Framework Upgrade Guide (Sep 6, 2023): Navigate to cutting-edge performance. Read More

--

--

Marcelo Domingues

🚀 Senior Software Engineer | Crafting Code & Words | Empowering Tech Enthusiasts ✨ 📲 LinkedIn: https://www.linkedin.com/in/marcelogdomingues/