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 a name. It can even be used as a parameter in a function. Lambda Expressions facilitate functional programming and simplify development greatly.
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 contain zero or more parameters in parentheses.
Arrow Token: The arrow token -> separates the parameters from the lambda body.
Body: The lambda's body 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
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();
}
}
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 code comments, which indicate 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();
}
}
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....
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....");
}
}
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));
}
}
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));
}
}
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 using 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.
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;
}
}
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
- Java 8 Functional Interfaces
- Java 8 Method References
- Java 8 Stream API
- Java 8 Optional Class
- Java 8 Collectors Class
- Java 8 StringJoiner Class
- Java 8 Static and Default Methods in Interface
- Guide to Java 8 forEach Method
- Handle NullPointerException in Controller, Service, and DAO Layer using Java 8 Optional Class
- How to Use Java 8 Stream API in Java Projects
- Migrating Source Code to Java 8
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.
ReplyDeleteHey Udaykiran you are right for with lambda example i missed to create thread object and pass runnable object to it. Good catch thank you.
DeleteDownload 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