Careeroza
Custom Functional Interfaces
@FunctionalInterface
You can write your own functional interface. Mark it with @FunctionalInterface: the compiler then fails if someone adds a second abstract method. default and static methods are allowed, because they are not abstract.
Example
A custom interface for discount rules, with a default method to chain two rules.
public class Main {
public static void main(String[] args) {
DiscountRule festive = amount -> amount * 0.10;
DiscountRule flat100 = amount -> amount >= 500 ? 100 : 0;
DiscountRule both = festive.plus(flat100);
double bill = 2000;
System.out.println("Festive: " + festive.discount(bill));
System.out.println("Flat: " + flat100.discount(bill));
System.out.println("Both: " + both.discount(bill));
System.out.println("Payable: " + (bill - both.discount(bill)));
}
}
@FunctionalInterface
interface DiscountRule {
double discount(double amount);
default DiscountRule plus(DiscountRule other) {
return amount -> this.discount(amount) + other.discount(amount);
}
}Output
Festive: 200.0 Flat: 100.0 Both: 300.0 Payable: 1700.0
Checked Exceptions in Lambdas
The built-in interfaces do not declare checked exceptions, so a lambda that calls Files.readString must catch IOException inside. A common fix is your own interface that throws Exception.
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
static <T, R> Function<T, R> unchecked(ThrowingFunction<T, R> f) {
return t -> {
try { return f.apply(t); }
catch (Exception e) { throw new RuntimeException(e); }
};
}
}
// usage
List<String> contents = paths.stream()
.map(ThrowingFunction.unchecked(Files::readString))
.toList();