Lambda expressions provide a concise way to represent anonymous functions in Java. They allow developers to treat code as data, passing functionality as arguments or returning them from methods. This feature enhances Java's expressiveness and supports functional programming paradigms introduced in Java 8.
Key Characteristics
- Supports functional programming style
- Enables automatic type inference for parameters
- Reduces boilerplate code compared to traditional approaches
- Suitable for interfaces with single abstract methods
Usage Scenarios
Lambda expressions work wherever functional interfaces are expected. A functional interface contains exactly one abstract method, making it compatible with lambda syntax.
Syntax Examples
() -> {}
() -> { System.out.println("Hello"); }
() -> System.out.println("Hello")
() -> { return 42; }
() -> 42
x -> x + 1
(x) -> x + 1
(int x) -> x + 1
(int x) -> { return x + 1; }
The left side of the arrow represents parameters (with optional type declarations), while the right side contains the implementation body. Curly braces can be omitted for single-expression bodeis.
Practical Implementation
// Traditional approach
Runnable task1 = new Runnable() {
public void run() {
System.out.println("Task 1 executing...");
}
};
// Lambda equivalent
Runnable task2 = () -> System.out.println("Task 2 executing...");
task2.run();
// With return values
Callable<String> processor = () -> "Processing complete";
System.out.println(processor.call());
Custom Functional Interfaces
public interface VehicleProducer {
String create();
}
public interface PersonRepository {
boolean save(Person entity);
}
// Simple implementation
VehicleProducer factory = () -> "car";
System.out.println(factory.create());
// Block implementation
VehicleProducer factory2 = () -> {
return "truck";
};
System.out.println(factory2.create());
// Parameterized version
PersonRepository storage = person -> {
return person.getAge() > 0;
};
Person individual = new Person(25, "Alice");
System.out.println(storage.save(individual));
Built-in Functional Interfaces
Java provides several standard functional interfaces in the java.util.function package:
- Supplier<T>: Produces a result of type T
- Consumer<T>: Consumes a value of type T
- BiConsumer<T,U>: Consumes two values of types T and U
- Function<T,R>: Transforms T into R
- UnaryOperator<T>: Transforms T into T
- BiFunction<T,U,R>: Transforms T and U into R
- BinaryOperator<T>: Combines two T values into another T
Standard Interface Usage
// Function transformation
Function<String, Integer> lengthCalculator = text -> text.length();
System.out.println(lengthCalculator.apply("example"));
// Supplier generation
Supplier<Integer> generator = () -> 100;
System.out.println(generator.get());
// Consumer processing
Consumer<String> printer = message -> System.out.println(message.length());
printer.accept("test");
// BiConsumer processing
BiConsumer<String, Integer> combiner = (text, number) ->
System.out.println(text.length() + number);
combiner.accept("sample", 5);
Method References
Method references provide even more concise syntax by referencing existing methods:
Static Method References
class TextProcessor {
public static String convertToUpper(String input) {
return input.toUpperCase();
}
public static void display(String content) {
System.out.println(content);
}
}
// Using method reference
Function<String, String> converter = TextProcessor::convertToUpper;
System.out.println(converter.apply("hello"));
Consumer<String> displayer = TextProcessor::display;
displayer.accept("message");
Instance Method References
class DataHandler {
public String process(String data) {
return data.toLowerCase();
}
}
DataHandler handler = new DataHandler();
Function<String, String> processor = handler::process;
System.out.println(processor.apply("WORLD"));
Constructor References
// Referencing constructors
Supplier<ArrayList<String>> listFactory = ArrayList::new;
ArrayList<String> newList = listFactory.get();
newList.add("item");
Function<Integer, StringBuilder> builderFactory = StringBuilder::new;
StringBuilder sb = builderFactory.apply(32);