引言
Java 8 引入的 Lambda 表达式和函数式接口,让 Java 具备了函数式编程的能力。函数式编程不是要替代面向对象,而是提供了一种更简洁、更具表达力的编程范式,特别适合集合操作、事件处理和异步编程。
一、Lambda 表达式
1.1 语法
// 完整语法
(Type1 param1, Type2 param2) -> { expression; return value; }
// 简化规则
// 1. 类型推断:参数类型可省略
(a, b) -> a + b
// 2. 单参数:括号可省略
name -> System.out.println(name)
// 3. 单表达式:花括号和 return 可省略
(a, b) -> a + b
// 4. 多行语句:需要花括号和 return
(a, b) -> {
int sum = a + b;
return sum;
}
1.2 常见用法
// 替代匿名内部类
// Before
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
// After
Runnable r2 = () -> System.out.println("hello");
// Comparator
Comparator<String> c1 = (a, b) -> a.length() - b.length();
// 自定义接口
@FunctionalInterface
interface StringProcessor {
String process(String input);
}
StringProcessor toUpper = s -> s.toUpperCase();
1.3 变量捕获
// Lambda 可以捕获 effectively final 的局部变量
String prefix = "Hello, "; // effectively final
Function<String, String> greeter = name -> prefix + name;
greeter.apply("Alice"); // "Hello, Alice"
// Lambda 内部不能修改捕获的变量
// prefix = "Hi, "; // 编译错误
二、函数式接口
2.1 @FunctionalInterface
// 函数式接口:只包含一个抽象方法的接口
@FunctionalInterface
public interface Converter<T, R> {
R convert(T input);
}
// 默认方法和静态方法不影响
@FunctionalInterface
public interface SmartConverter<T, R> {
R convert(T input);
default R convertOrDefault(T input, R defaultValue) {
return input == null ? defaultValue : convert(input);
}
static <T> SmartConverter<T, T> identity() {
return t -> t;
}
}
2.2 JDK 内置函数式接口
| 接口 |
参数 |
返回 |
用途 |
示例 |
Supplier<T> |
无 |
T |
提供值 |
() -> new Config() |
Consumer<T> |
T |
void |
消费值 |
s -> log.info(s) |
BiConsumer<T,U> |
T,U |
void |
消费两个值 |
(k,v) -> map.put(k,v) |
Function<T,R> |
T |
R |
转换 |
s -> s.length() |
BiFunction<T,U,R> |
T,U |
R |
双参数转换 |
(a,b) -> a + b |
UnaryOperator<T> |
T |
T |
一元操作 |
s -> s.trim() |
BinaryOperator<T> |
T,T |
T |
二元操作 |
(a,b) -> a + b |
Predicate<T> |
T |
boolean |
判断 |
s -> s.isEmpty() |
BiPredicate<T,U> |
T,U |
boolean |
双参数判断 |
(a,b) -> a.equals(b) |
基本类型特化(避免装箱拆箱):
IntSupplier supplier = () -> 42; // int -> int
IntFunction<String> func = i -> "N" + i; // int -> String
IntPredicate pred = i -> i > 0; // int -> boolean
IntConsumer consumer = i -> log(i); // int -> void
IntUnaryOperator op = i -> i * 2; // int -> int
IntBinaryOperator binOp = (a, b) -> a + b; // int,int -> int
三、方法引用
3.1 四种形式
| 形式 |
语法 |
等价 Lambda |
| 静态方法引用 |
ClassName::staticMethod |
a -> ClassName.staticMethod(a) |
| 实例方法引用 |
instance::method |
a -> instance.method(a) |
| 类型方法引用 |
ClassName::method |
(obj, a) -> obj.method(a) |
| 构造器引用 |
ClassName::new |
() -> new ClassName() |
3.2 示例
// 1. 静态方法引用
Function<String, Integer> parser = Integer::parseInt;
// 2. 实例方法引用
String prefix = "Hello, ";
Function<String, String> greeter = prefix::concat;
// 3. 类型方法引用(最常用)
Function<String, Integer> lengthFn = String::length;
BiPredicate<String, String> equalsFn = String::equals;
// 4. 构造器引用
Supplier<List<String>> listFactory = ArrayList::new;
Function<Integer, int[]> arrayFactory = int[]::new;
3.3 方法引用 vs Lambda
// 优先使用方法引用,更简洁
list.forEach(System.out::println); // 方法引用
list.forEach(s -> System.out.println(s)); // Lambda,稍冗长
// 方法引用无法表达时使用 Lambda
list.stream().map(s -> s + "!"); // Lambda
四、函数组合
4.1 Function 组合
Function<String, String> trim = String::trim;
Function<String, String> toUpper = String::toUpperCase;
Function<String, String> addPrefix = s -> "Hello, " + s;
// andThen:先执行当前,再执行参数
Function<String, String> pipeline = trim.andThen(toUpper).andThen(addPrefix);
String result = pipeline.apply(" alice "); // "Hello, ALICE"
// compose:先执行参数,再执行当前
Function<String, String> pipeline2 = addPrefix.compose(toUpper).compose(trim);
String result2 = pipeline2.apply(" alice "); // "Hello, ALICE"
4.2 Predicate 组合
Predicate<String> nonEmpty = s -> !s.isEmpty();
Predicate<String> shortStr = s -> s.length() < 10;
Predicate<String> startsWithA = s -> s.startsWith("A");
// and / or / negate
Predicate<String> combined = nonEmpty.and(shortStr).and(startsWithA.negate());
combined.test("Hello"); // true
combined.test("ABCDEF"); // false
4.3 Consumer 组合
Consumer<String> log = s -> System.out.println("LOG: " + s);
Consumer<String> audit = s -> System.out.println("AUDIT: " + s);
Consumer<String> logAndAudit = log.andThen(audit);
logAndAudit.accept("user login");
// LOG: user login
// AUDIT: user login
五、高阶函数实战
5.1 策略模式
// 传统策略模式需要多个类
// 函数式策略模式:用 Map + Lambda 替代
Map<String, Function<Double, Double>> strategies = Map.of(
"regular", p -> p * 0.95,
"vip", p -> p * 0.8,
"svip", p -> p * 0.7
);
double discounted = strategies.getOrDefault(userLevel, p -> p).apply(originalPrice);
5.2 装饰器模式
// 函数式装饰器
Function<String, String> base = s -> s;
Function<Function<String, String>, Function<String, String>> trimDecorator =
fn -> s -> fn.apply(s.trim());
Function<Function<String, String>, Function<String, String>> upperDecorator =
fn -> s -> fn.apply(s.toUpperCase());
Function<Function<String, String>, Function<String, String>> prefixDecorator =
fn -> s -> fn.apply(">> " + s);
Function<String, String> decorated = trimDecorator
.andThen(upperDecorator)
.andThen(prefixDecorator)
.apply(base);
decorated.apply(" hello "); // ">> HELLO"
5.3 惰性求值
public class Lazy<T> {
private Supplier<T> supplier;
private T value;
private boolean computed = false;
public Lazy(Supplier<T> supplier) {
this.supplier = supplier;
}
public T get() {
if (!computed) {
value = supplier.get();
computed = true;
supplier = null;
}
return value;
}
public <R> Lazy<R> map(Function<T, R> fn) {
return new Lazy<>(() -> fn.apply(this.get()));
}
public <R> Lazy<R> flatMap(Function<T, Lazy<R>> fn) {
return new Lazy<>(() -> fn.apply(this.get()).get());
}
}
// 使用
Lazy<String> lazy = new Lazy<>(() -> {
System.out.println("Computing...");
return "hello";
});
// 此时未计算
String result = lazy.get(); // "Computing..." -> "hello"
String again = lazy.get(); // 直接返回缓存值,不再计算
5.4 回调模式
public <T> void executeAsync(Supplier<T> task, Consumer<T> onSuccess, Consumer<Exception> onError) {
new Thread(() -> {
try {
T result = task.get();
onSuccess.accept(result);
} catch (Exception e) {
onError.accept(e);
}
}).start();
}
// 使用
executeAsync(
() -> fetchData(),
result -> System.out.println("Success: " + result),
error -> System.err.println("Error: " + error.getMessage())
);
六、常见陷阱
6.1 Lambda 中的 this
public class Demo {
private String name = "Outer";
public void test() {
// Lambda 中的 this 指向外部类实例
Runnable r = () -> System.out.println(this.name); // "Outer"
// 匿名内部类中的 this 指向匿名类实例
Runnable r2 = new Runnable() {
private String name = "Inner";
public void run() {
System.out.println(this.name); // "Inner"
}
};
}
}
6.2 Lambda 与受检异常
// Lambda 中受检异常必须处理
list.forEach(s -> {
try {
Files.readString(Path.of(s));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
// 封装工具方法
private static <T> Supplier<T> unchecked(Callable<T> callable) {
return () -> {
try {
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
6.3 Lambda 与重载歧义
// 编译错误:Lambda 类型推断歧义
execute(() -> {}); // 是 Runnable 还是 Callable<Void>?
// 显式指定类型
execute((Runnable) () -> {});
execute((Callable<Void>) () -> null);
总结
| 要点 |
建议 |
| Lambda |
优先替代匿名内部类 |
| 方法引用 |
比 Lambda 更简洁时使用 |
| 函数式接口 |
优先使用 JDK 内置接口 |
| 函数组合 |
andThen/compose 构建处理管道 |
| 变量捕获 |
effectively final,不要 hack |
| 受检异常 |
Lambda 中必须 try-catch 或包装 |