1. 引言
本文系统梳理了 Java 8 中核心函数式接口的用法、典型场景及在标准库中的应用,为日常开发提供实用参考。
2. Java 8 中的 Lambda 表达式
Java 8 引入的 Lambda 表达式是语法层面的重大革新。Lambda 本质是匿名函数,可作为一等公民使用(如作为参数传递或方法返回值)。
在 Java 8 之前,封装单一功能通常需要创建完整类,导致大量样板代码。Lambda 彻底改变了这种模式,用更简洁的方式表达函数逻辑。
⚠️ 关于 Lambda 最佳实践,可参考《Lambda 表达式与函数式接口:技巧与最佳实践》一文。本文聚焦 java.util.function 包中的核心接口。
3. 函数式接口
所有函数式接口都应添加 @FunctionalInterface 注解。这有两个作用:
明确声明接口用途
让编译器检查接口是否符合函数式接口规范
核心规则:任何包含 SAM(Single Abstract Method)的接口都是函数式接口,其实现可被 Lambda 表达式替代。
✅ 注意:Java 8 的 default 方法不计入抽象方法,因此函数式接口可包含多个默认方法(参考 Function 接口文档)。
4. Function 接口
最通用的函数式接口是 Function
public interface Function
典型应用场景
Map.computeIfAbsent 方法展示了 Function 的经典用法:当键不存在时,通过函数计算新值:
Map
Integer value = nameMap.computeIfAbsent("John", s -> s.length());
💡 技巧:当 Lambda 表达式仅调用现有方法时,可直接替换为方法引用:
Integer value = nameMap.computeIfAbsent("John", String::length);
函数组合
Function 提供了 compose 方法实现函数链式调用:
Function
Function
Function
assertEquals("'5'", quoteIntToString.apply(5));
quoteIntToString 先执行 intToString 再执行 quote。
5. 原生类型函数特化
由于泛型不支持原生类型,Java 提供了针对 double/int/long 的特化接口:
输入类型
返回类型
接口示例
原生类型
泛型
IntFunction
泛型
原生类型
ToIntFunction
原生类型
原生类型
DoubleToIntFunction, IntToLongFunction 等
自定义特化接口
当需要处理 short 到 byte 转换时,可自定义接口:
@FunctionalInterface
public interface ShortToByteFunction {
byte applyAsByte(short s);
}
使用示例:
public byte[] transformArray(short[] array, ShortToByteFunction function) {
byte[] transformedArray = new byte[array.length];
for (int i = 0; i < array.length; i++) {
transformedArray[i] = function.applyAsByte(array[i]);
}
return transformedArray;
}
// 调用示例
short[] array = {(short) 1, (short) 2, (short) 3};
byte[] transformedArray = transformArray(array, s -> (byte) (s * 2));
byte[] expectedArray = {(byte) 2, (byte) 4, (byte) 6};
assertArrayEquals(expectedArray, transformedArray);
6. 双参数函数特化
处理双参数 Lambda 需使用带 "Bi" 前缀的接口:
BiFunction
ToDoubleBiFunction
典型应用:Map 批量更新
Map.replaceAll 使用 BiFunction 计算新值:
Map
salaries.put("John", 40000);
salaries.put("Freddy", 30000);
salaries.put("Samuel", 50000);
// 非 Freddy 员工加薪 10000
salaries.replaceAll((name, oldValue) ->
name.equals("Freddy") ? oldValue : oldValue + 10000);
7. Supplier 接口
Supplier 表示无参函数,常用于延迟计算或生成序列。
延迟计算示例
public double squareLazy(Supplier
return Math.pow(lazyValue.get(), 2);
}
// 模拟耗时计算
Supplier
Uninterruptibles.sleepUninterruptibly(1000, TimeUnit.MILLISECONDS);
return 9d;
};
Double valueSquared = squareLazy(lazyValue);
序列生成:斐波那契数列
int[] fibs = {0, 1};
Stream
int result = fibs[1];
int fib3 = fibs[0] + fibs[1];
fibs[0] = fibs[1];
fibs[1] = fib3;
return result;
});
⚠️ 注意:Lambda 中使用的外部变量必须是 effectively final,因此用数组而非变量保存状态。
原生类型特化
BooleanSupplier/DoubleSupplier/LongSupplier/IntSupplier 对应返回原生类型的场景。
8. Consumer 接口
Consumer 表示接收参数但不返回值的操作(副作用操作)。
典型应用:遍历处理
List
names.forEach(name -> System.out.println("Hello, " + name));
双参数版本:BiConsumer
遍历 Map 条目时特别有用:
Map
ages.put("John", 25);
ages.put("Freddy", 24);
ages.put("Samuel", 30);
ages.forEach((name, age) ->
System.out.println(name + " is " + age + " years old"));
原生类型特化
DoubleConsumer/IntConsumer/LongConsumer:单原生参数
ObjDoubleConsumer/ObjIntConsumer/ObjLongConsumer:泛型+原生参数
9. Predicate 接口
Predicate 表示返回布尔值的函数,常用于条件判断。
集合过滤示例
List
List
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
原生类型特化
IntPredicate/DoublePredicate/LongPredicate 处理原生类型参数。
10. Operator 接口
Operator 是特殊函数:输入和输出类型相同。
UnaryOperator:单参数操作
List.replaceAll 要求转换前后类型一致:
List
names.replaceAll(String::toUpperCase); // 方法引用简化
BinaryOperator:归约操作
Stream.reduce 的经典应用:
List
int sum = values.stream()
.reduce(0, (i1, i2) -> i1 + i2);
✅ 关键要求:BinaryOperator 必须满足结合律(op(a, op(b,c)) == op(op(a,b), c)),这是并行归约的基础。
原生类型特化
单参数:DoubleUnaryOperator/IntUnaryOperator/LongUnaryOperator
双参数:DoubleBinaryOperator/IntBinaryOperator/LongBinaryOperator
11. 遗留函数式接口
Java 8 之前的接口只要符合 SAM 规范,也可用作 Lambda。典型代表:
Runnable:并发任务
Callable:带返回值的并发任务
简化并发代码
Thread thread = new Thread(() -> System.out.println("Hello From Another Thread"));
thread.start();
12. 总结
本文系统梳理了 Java 8 核心函数式接口:
基础接口:Function/Supplier/Consumer/Predicate
特化接口:针对原生类型和双参数场景的优化版本
操作接口:UnaryOperator/BinaryOperator 及其特化
遗留兼容:Runnable/Callable 等历史接口的 Lambda 支持
掌握这些接口能显著提升代码简洁性和表达力,是函数式编程的基石。