Java Functional Interfaces and Stream API Fundamentals
Functional Programming in Java
Java functional programming concepts are essential for modern Java EE development, particularly within Spring Framework implementations. Key components include:
- Functional interfaces
- Stream API
- Lambda expressions
- Implementation patterns
Core packages for functional programming:
java.util.function
java.util.stream
The @FunctionalInterface annotation resides in java.lang, indicating its fundamental role.
Functional Interfaces
Definition and Structure
A functional interface must:
- Be annotated with
@FunctionalInterface - Contain exactly one public abstract method
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
Example interface declaration:
@FunctionalInterface
public interface BinaryOperator {
int execute(int x, int y);
// Permitted elements
static final int MODE_A = 1;
private static int generateRandom() {
return new Random().nextInt(100);
}
default void displayInputs(int x, int y) {
System.out.printf("Operands: %d, %d", x, y);
}
}
Implementation Methods
Five implementation approaches:
// Traditional class
class Adder {
public int sum(int a, int b) {
return a + b;
}
}
// Constructor reference
@FunctionalInterface
interface ShapeFactory {
Shape create(int width, int height);
}
class Shape {
int width;
int height;
public Shape(int w, int h) {
width = w;
height = h;
}
}
Implementation examples:
// 1. Traditional class
BinaryOperator op1 = new Adder()::sum;
// 2. Lambda expression
BinaryOperator op2 = (a, b) -> a + b;
// 3. Anonymous class
BinaryOperator op3 = new BinaryOperator() {
@Override
public int execute(int a, int b) {
return a * b;
}
};
// 4. Method reference
BinaryOperator op4 = Integer::sum;
// 5. Constructor reference
ShapeFactory factory = Shape::new;
Shape rect = factory.create(10, 20);
Stream API Fundamentals
Core Characteristics
Stream pipelines consist of:
- Source (collections, arrays, I/O)
- Zero or more intermediate operations
- Terminal operation producing result
Key properties:
- Lazy evaluation - computations trigger only at terminal operations
- Single-use - streams can't be reused after terminal operation
- AutoCloseable for I/O-based streams
Example pipeline:
int totalWeight = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(Widget::getWeight)
.sum();
Advantages and Limitations
Advantages:
- Concise syntax for data transformations
- Built-in paralel processing capabilities
Limitations:
- Debugging complexity
- Potential performance overhead with small datasets
- Database operations often outperform in-memory stream processing
Practical Applications
Common usage scenarios:
- Data transformation pipelines
- Collection processing with chained operations
- Concurrent processing via parallel streams
Recommended practices:
- Prefer database operations for aggregation tasks
- Use streams for intermediate transformations
- Limit parallel streams for large datasets