Lambda Expressions and Functional Interfaces
Lambda expressions provide a compact way to represent behavior in Java. They are especially useful with functional interfaces, collections, streams, callbacks, and APIs that expect small pieces of executable logic.
What is a Lambda Expression?
A lambda expression is a compact way to provide the implementation of a functional interface method.
It is commonly used when behavior needs to be passed as data—for example, when sorting, filtering, processing collections, or supplying a callback.
Basic Syntax
(parameters) -> { code block }Example:
(int a, int b) -> { return a + b; }If the body contains a single expression, Java can often infer even more:
(a, b) -> a + bFunctional Interfaces
A functional interface contains exactly one abstract method. Lambda expressions can be used wherever an instance of such an interface is expected.
@FunctionalInterface
interface Greeting {
void sayHello();
}Using a lambda:
Greeting g = () -> System.out.println("Hello!");
g.sayHello();The @FunctionalInterface annotation is optional, but useful because the compiler checks that the interface still has only one abstract method.
Built-in Functional Interfaces
Java provides many ready-to-use functional interfaces in java.util.function.
| Interface | Input | Output | Typical method |
|---|---|---|---|
Consumer<T> | One value | Nothing | accept |
Supplier<T> | Nothing | One value | get |
Function<T,R> | One value | One value | apply |
Predicate<T> | One value | boolean | test |
Consumer
Consumer<String> printer =
s -> System.out.println(s);
printer.accept("Printing from Consumer!");Supplier
Supplier<Integer> answer = () -> 42;
System.out.println(answer.get());Function
Function<String, Integer> lengthFunction =
str -> str.length();
System.out.println(lengthFunction.apply("Apple"));Predicate
Predicate<Integer> isEven =
x -> x % 2 == 0;
System.out.println(isEven.test(10));Why Use Lambdas?
- They reduce boilerplate code.
- They make behavior-oriented APIs easier to read.
- They work naturally with collections and streams.
- They support composition through functional interfaces.
List Example
List<String> names =
Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(
name -> System.out.println("Hi " + name)
);Lambda vs Anonymous Class
Before lambdas, small behaviors were often written using anonymous classes.
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Running");
}
};
Runnable r2 =
() -> System.out.println("Running");The lambda version is shorter while expressing the same single behavior.
Important Limitations
- A lambda can target only a functional interface.
- Local variables captured by a lambda must be final or effectively final.
thisinside a lambda refers to the surrounding object, unlike many anonymous-class cases.- Overusing lambdas can hurt readability when the logic becomes too large or complex.
Conclusion
Lambda expressions are most useful for short pieces of behavior. Start with simple examples, then use them with collections, event handlers, and the Stream API.