Mastering Java Lambda Expressions: A Deep Dive
This article explains what Java lambda expressions are, why they were added in Java 8, their syntax and classification, how they relate to functional interfaces, and demonstrates practical examples including anonymous inner‑class refactoring, stream usage, method references, and custom functional interfaces.
What is a Lambda Expression?
A lambda expression can be understood as an anonymous function that allows a function to be passed as an argument to another function; it represents a piece of code that can be supplied as a parameter, embodying functional programming concepts.
Why Were Lambdas Introduced?
Java developers saw other languages (e.g., JavaScript, Python) using closures or lambdas and complained that the most widely used language lacked functional programming support. In response, Java 8 introduced lambda expressions.
Lambda expressions make programming more efficient.
Refactoring Example: From Anonymous Class to Lambda
Original code using a concrete class and a separate Runnable implementation:
package com.isea.java;
public class TestLambda {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
thread.close();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello");
}
}Refactored with an anonymous inner class (note the inline comments have been translated to English):
package com.isea.java;
public class TestLambda {
public static void main(String[] args) {
new Thread(new Runnable() {
// Here we create a new instance of the interface and implement its method.
// The overridden run() method is passed into the constructor.
@Override
public void run() {
System.out.println("Hello");
}
}).start();
}
}Further simplified by assigning the anonymous Runnable to a variable:
package com.isea.java;
public class TestLambda {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
});
thread.start();
}
}Most concise form using a lambda expression:
package com.isea.java;
public class TestLambda {
public static void main(String[] args) {
new Thread(() -> System.out.println("Hello")).start();
}
}Result: the code is dramatically shorter while preserving the same behavior.
Lambda Syntax
([parameter list]) -> {lambda body}
Copy the parentheses, write the arrow "->", and place the implementation logic inside braces.
Key annotations and modifiers:
@FunctionalInterface
default methods
static methods
Characteristics: the "->" separates parameters from the implementation; the parentheses contain the parameters, and the braces contain the logic.
Lambda Classification
No‑arg, no return value
package com.isea.java;
public class TestLambda {
public static void main(String[] args) {
new Thread(() -> System.out.println("Hello")); // () cannot be omitted; {} can be omitted for a single statement
}
}With arguments, no return value
package com.isea.java;
import java.util.ArrayList;
public class TestLambda {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("AAAAA");
list.add("BBBBB");
list.add("CCCCC");
list.add("DDDDD");
// Parameter type can be inferred; parentheses can be omitted for a single parameter
list.forEach(t -> System.out.print(t + "\t"));
// Or using a method reference: list.forEach(System.out::println);
// Output: AAAAA BBBBB CCCCC DDDDD
}
}No‑arg, returns a value
package com.isea.java;
import java.util.Random;
import java.util.stream.Stream;
public class TestLambda {
public static void main(String[] args) {
Random random = new Random();
Stream<Integer> stream = Stream.generate(() -> random.nextInt(100));
stream.forEach(t -> System.out.println(t)); // Generates an infinite stream of integers < 100
}
}With arguments and a return value
Example: sorting students by name using a Collator:
package com.isea.java;
import java.text.Collator;
import java.util.TreeSet;
public class TestLambda {
public static void main(String[] args) {
Collator collator = Collator.getInstance();
TreeSet<Student> set = new TreeSet<>((s1, s2) -> collator.compare(s1.getName(), s2.getName()));
set.add(new Student(10, "Zhang Fei"));
set.add(new Student(3, "Zhou Yu"));
set.add(new Student(1, "Song Jiang"));
set.forEach(student -> System.out.println(student));
}
}
class Student {
private int id;
private String name;
public Student(int id, String name) { this.id = id; this.name = name; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "Student{id=" + id + ", name='" + name + "'}";
}
}What Is a Functional Interface?
A functional interface (also called a SAM – Single Abstract Method interface) is an interface that contains exactly one abstract method, though it may have default or static methods and the methods inherited from Object. Since Java 8, the @FunctionalInterface annotation can be used to mark such interfaces.
@FunctionalInterface
public interface Runnable {
void run();
}Only variables of a functional‑interface type can be assigned a lambda expression. The lambda is implicitly converted to an instance of the functional interface.
For example, the Thread constructor expects a Runnable implementation, so a lambda can be passed directly:
new Thread(() -> System.out.println("hello")).start();The compiler infers that the lambda provides the implementation of Runnable.run(). The parameter‑return correspondence holds: if the abstract method has parameters and a return value, the lambda must match that signature.
Creating a Custom Functional Interface
package com.isea.java;
@FunctionalInterface
public interface IMyInterface {
void study();
}
public class TestIMyInterface {
public static void main(String[] args) {
IMyInterface iMyInterface = () -> System.out.println("I like study");
iMyInterface.study(); // The lambda assigns the method body to the functional interface
}
}The Four Core Functional Interfaces in java.util.function
These interfaces are frequently used; many others exist in the same package.
Consumer<T> : void accept(T t) – takes an argument, returns nothing.
Supplier<T> : T get() – no arguments, returns a value.
Predicate<T> : boolean test(T t) – takes an argument, returns a boolean.
Function<T,R> : R apply(T t) – takes an argument, returns a result.
Example using Consumer to adjust salaries below 10,000:
package com.isea.java;
import java.util.HashMap;
public class TestLambda {
public static void main(String[] args) {
HashMap<String, Double> map = new HashMap<>();
map.put("Zhou Yu", 9000.0);
map.put("Song Jiang", 12000.0);
map.put("Zhang Fei", 8000.0);
map.forEach((k, v) -> {
if (v < 10000.0) map.put(k, 10000.0);
});
map.forEach((k, v) -> System.out.print(k + ":" + v + "\t\t"));
// Output: Zhang Fei:10000.0 Zhou Yu:10000.0 Song Jiang:12000.0
}
}Method References
When a lambda merely calls an existing method, a method reference can make the code even shorter.
Constructor reference : Supplier<Student> s = Student::new; Class name :: instance method :
TreeSet<String> set = new TreeSet<>(String::compareTo);Object :: instance method : set.forEach(System.out::println); Class name :: static method : Stream<Double> stream = Stream.generate(Math::random); These references rely on the same parameter‑to‑method mapping rules as lambdas.
Finally, the author reflects on the writing process, noting that deep, incremental effort is required to truly understand seemingly superficial concepts.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
