Detailed Explanation of Lambda Expressions and Functional Programming in Java
I. Overview of Lambda Expressions
Lambda expressions are a core feature introduced in Java 8, essentially representing an anonymous function (method). They allow functions to be passed as method parameters, simplifying code writing. Their emergence addressed the issue of code redundancy in anonymous inner classes.
II. Syntax Structure of Lambda Expressions
Full syntax format: (parameter list) -> { code block }
- Parameter list: Matches the interface method parameters; types can be omitted (type inference).
->: Lambda operator, separating parameters and code.- Code block: Specific implementation logic;
{}and;can be omitted for single-line code.
III. Evolution Steps of Lambda Expressions
- Traditional anonymous inner class approach:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
- Lambda simplification process:
- Step 1: Retain core parameters and method body
Runnable r = () -> { System.out.println("Hello"); }; - Step 2: Omit
{}and;for single-line codeRunnable r = () -> System.out.println("Hello");
IV. Functional Interfaces
Lambda expressions must rely on functional interfaces, which is key to understanding their nature:
- Definition: An interface containing only one abstract method (may include default/static methods).
@FunctionalInterfaceannotation: Compiler verifies if the interface meets the specification.- Common built-in functional interfaces:
Consumer<T>: Consumer type, accepts parameters with no return value.Supplier<T>: Supplier type, no parameters but returns a result.Function<T,R>: Function type, accepts parameters and returns a result.Predicate<T>: Predicate type, accepts parameters and returns a boolean.
V. Method References
Syntactic sugar for lambda expressions, further simplifying code:
- Static method reference:
ClassName::staticMethodlist.forEach(System.out::println); - Instance method reference:
instance::methodString str = "test"; Supplier<Integer> s = str::length; - Constructor reference:
ClassName::newSupplier<List<String>> supplier = ArrayList::new;
VI. Implementation Principle of Lambda Expressions
- Compilation phase: The compiler generates private static methods to store the lambda body code.
- Runtime phase: Uses the
invokedynamicinstruction for dynamic method binding. - Advantage: Avoids generating additional class files like anonymous inner classes, improving performance.
VII. Practical Application Scenarios
- Collection iteration:
list.forEach(item -> System.out.println(item)); - Thread creation:
new Thread(() -> System.out.println("Thread executing")).start(); - Conditional filtering:
list.stream().filter(s -> s.length() > 3).collect(Collectors.toList());
VIII. Considerations
- Variable capture: Lambda can only reference final or effectively final local variables.
thisreference: Thethisinside a lambda refers to the enclosing class, unlike in inner classes.- Performance considerations: Using lambda expressions in frequently called hot code can yield better performance improvements.