Java中的FunctionalInterface(函数式接口)是Java 8引入的核心特性之一,主要用于支持Lambda表达式和函数式编程风格。以下从定义、规则、注解、常见例子、应用场景、自定义方法及注意事项等方面进行详解:
1. 定义与核心规则
- 定义:函数式接口是仅包含一个抽象方法的接口。Java通过
@FunctionalInterface注解显式标记此类接口,编译器会强制检查是否符合规则。 - 核心规则:
- 接口中只能有一个抽象方法(抽象方法数量=1)。
- 可包含多个默认方法(default methods)和静态方法(static methods)。
- 可包含来自
Object类的默认方法(如equals()、hashCode()、toString()等),这些方法不计入抽象方法计数。 - 若接口违反规则(如多个抽象方法),即使标记
@FunctionalInterface,编译器也会报错。
2. @FunctionalInterface注解
- 作用:显式声明接口为函数式接口,编译器会验证接口是否满足函数式接口的条件。
- 非必须:即使不添加该注解,只要接口符合函数式接口的定义,仍可作为函数式接口使用(如Lambda的目标类型)。
- 示例:
@FunctionalInterface
public interface MyFunctionalInterface {
void doSomething(); // 唯一抽象方法
default void defaultMethod() {
System.out.println("Default method");
}
static void staticMethod() {
System.out.println("Static method");
}
}
3. 常见内置函数式接口
Java在java.util.function包中提供了大量内置函数式接口,它们为函数式编程提供了核心支持,允许将行为作为参数传递、组合或返回,覆盖常见操作:
Function<T, R>:- 定义:接受一个输入参数
T,返回一个结果R。 - 核心方法:
R apply(T t):执行转换操作。default <V> Function<T, V> andThen(Function<? super R, ? extends V> after):链式调用,先执行当前函数,再执行after函数。default <V> Function<V, R> compose(Function<? super V, ? extends T> before):链式调用,先执行before函数,再执行当前函数。
- 用法示例:
// 字符串转大写 Function<String, String> toUpper = String::toUpperCase; System.out.println(toUpper.apply("hello")); // 输出: HELLO // 链式调用:先转大写,再截取前3个字符 Function<String, String> truncate = str -> str.substring(0, Math.min(str.length(), 3)); Function<String, String> pipeline = toUpper.andThen(truncate); System.out.println(pipeline.apply("world")); // 输出: WOR - 应用场景:
- 数据转换(如类型转换、格式化)。
- 流处理(
Stream.map())。 - 函数组合(构建复杂逻辑)。
- 定义:接受一个输入参数
- BiFunction<T, U, R>:
- 定义:接受两个输入参数
T和U,返回一个结果R。 - 核心方法:
R apply(T t, U u):执行双参数转换操作。default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after):链式调用,先执行当前函数,再执行after函数。
- 用法示例:
// 计算两个数的和并加5 BiFunction<Integer, Integer, Integer> addAndPlusFive = (a, b) -> a + b + 5; System.out.println(addAndPlusFive.apply(2, 3)); // 输出: 10 // 链式调用:先求和,再转换为字符串 BiFunction<Integer, Integer, Integer> sum = Integer::sum; Function<Integer, String> toString = Object::toString; BiFunction<Integer, Integer, String> sumToString = sum.andThen(toString); System.out.println(sumToString.apply(4, 5)); // 输出: "9" - 应用场景:
- 双参数计算(如数学运算、合并数据)。
- 复杂对象构造(如根据两个字段创建对象)。
- 函数组合(与
Function配合使用)。
- 定义:接受两个输入参数
Consumer<T>:- 定义:接受一个输入参数
T,不返回结果(执行副作用操作)。 - 核心方法:
void accept(T t):执行操作。default Consumer<T> andThen(Consumer<? super T> after):链式调用,先执行当前操作,再执行after操作。
- 用法示例:
// 打印字符串 Consumer<String> printer = System.out::println; printer.accept("Hello"); // 输出: Hello // 链式调用:先打印,再记录日志 Consumer<String> logger = str -> System.out.println("Log: " + str); Consumer<String> combined = printer.andThen(logger); combined.accept("World"); // 输出: World \n Log: World - 应用场景:
- 遍历集合(
Stream.forEach())。 - 日志记录。
- 资源操作(如关闭文件、释放锁)。
- 遍历集合(
- 定义:接受一个输入参数
- BiConsumer<T, U>:
- 定义:接受两个输入参数
T和U,不返回结果(执行副作用操作)。 - 核心方法:
void accept(T t, U u):执行双参数操作。default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after):链式调用,先执行当前操作,再执行after操作。
- 用法示例:
// 打印两个数的和 BiConsumer<Integer, Integer> printSum = (a, b) -> System.out.println(a + b); printSum.accept(2, 3); // 输出: 5 // 链式调用:先打印和,再打印积 BiConsumer<Integer, Integer> printProduct = (a, b) -> System.out.println(a * b); BiConsumer<Integer, Integer> combined = printSum.andThen(printProduct); combined.accept(4, 5); // 输出: 9 \n 20 - 应用场景:
- 双参数操作(如更新对象属性、记录日志)。
- 事件处理(如GUI中的按钮点击事件)。
- 定义:接受两个输入参数
Supplier<T>:- 定义:不接受输入参数,返回一个结果
T(延迟计算或生成值)。 - 核心方法:
T get():生成并返回结果。
- 用法示例:
// 生成随机数 Supplier<Double> randomSupplier = Math::random; System.out.println(randomSupplier.get()); // 输出: 0.xxx // 延迟初始化 Supplier<List<String>> listSupplier = ArrayList::new; List<String> list = listSupplier.get(); // 仅在需要时创建对象 - 应用场景:
- 延迟计算(如按需加载资源)。
- 生成默认值(如
Optional.orElseGet())。 - 工厂模式(创建对象)。
- 定义:不接受输入参数,返回一个结果
Predicate<T>:-
定义:接受一个输入参数
T,返回一个布尔值。 -
核心方法:
boolean test(T t):执行判断操作,返回true或false。default Predicate<T> and(Predicate<? super T> other):逻辑与组合,返回一个新的Predicate,只有当当前Predicate和other都返回true时,新Predicate才返回true。default Predicate<T> or(Predicate<? super T> other):逻辑或组合,返回一个新的Predicate,只要当前Predicate或other中有一个返回true,新Predicate就返回true。default Predicate<T> negate():逻辑非操作,返回一个新的Predicate,其判断结果与当前Predicate相反。
-
用法示例:
// 判断字符串是否为空 Predicate<String> isNotEmpty = str -> str != null && !str.isEmpty(); System.out.println(isNotEmpty.test("Hello")); // 输出: true System.out.println(isNotEmpty.test("")); // 输出: false // 组合多个Predicate Predicate<String> isLongerThan5 = str -> str.length() > 5; Predicate<String> combinedPredicate = isNotEmpty.and(isLongerThan5); System.out.println(combinedPredicate.test("Hello World")); // 输出: true System.out.println(combinedPredicate.test("Hi")); // 输出: false -
应用场景:
- 过滤集合元素(如
Stream.filter())。 - 条件判断(如
if语句中的条件)。
- 过滤集合元素(如
-
- BiPredicate<T, U>:
-
定义:接受两个输入参数
T和U,返回一个布尔值。 -
核心方法:
boolean test(T t, U u):执行双参数判断操作,返回true或false。- 类似
Predicate,也提供了and、or和negate方法用于逻辑组合。
-
用法示例:
// 判断两个整数是否相等 BiPredicate<Integer, Integer> isEqual = (a, b) -> a.equals(b); System.out.println(isEqual.test(1, 1)); // 输出: true System.out.println(isEqual.test(1, 2)); // 输出: false -
应用场景:
- 双参数条件判断(如比较两个对象是否相等)。
-
UnaryOperator<T>:-
定义:继承自
Function<T, T>,接受一个输入参数T,返回同类型的输出T。 -
核心方法:
T apply(T t):执行转换操作,输入和输出类型相同。
-
用法示例:
// 字符串转大写 UnaryOperator<String> toUpper = String::toUpperCase; System.out.println(toUpper.apply("hello")); // 输出: HELLO -
应用场景:
- 类型转换(输入和输出类型相同)。
- 对象方法调用(如调用对象的某个方法并返回自身)。
-
BinaryOperator<T>:-
定义:继承自
BiFunction<T, T, T>,接受两个同类型的输入参数T,返回同类型的输出T。 -
核心方法:
T apply(T t1, T t2):执行双参数转换操作,输入和输出类型相同。
-
用法示例:
// 计算两个整数的和 BinaryOperator<Integer> sum = Integer::sum; System.out.println(sum.apply(2, 3)); // 输出: 5 -
应用场景:
- 双参数计算(输入和输出类型相同)。
- 合并两个对象(如合并两个集合)。
-
java.util.function包还提供了一系列针对基本数据类型(如int、long、double)的特化接口,如IntFunction、LongFunction、DoubleFunction、IntConsumer、LongConsumer、DoubleConsumer等。这些接口减少了自动装箱和拆箱的开销,提高了性能。
示例:
// IntFunction示例:接受一个int参数,返回一个String
IntFunction<String> intToString = Object::toString;
System.out.println(intToString.apply(123)); // 输出: "123"
// DoubleConsumer示例:接受一个double参数,不返回结果
DoubleConsumer printDouble = System.out::println;
printDouble.accept(3.14); // 输出: 3.14
4. 与Lambda表达式的关联
- Lambda表达式是函数式接口的实例,用于实现其唯一抽象方法。
- 语法:
(参数) -> 表达式或(参数) -> { 代码块 }。 - 示例:
// 使用Function接口
Function<Integer, String> intToString = num -> "Number: " + num;
System.out.println(intToString.apply(42)); // 输出:Number: 42
// 使用Predicate接口
Predicate<String> isEmpty = str -> str == null || str.isEmpty();
System.out.println(isEmpty.test("")); // 输出:true
5. 自定义函数式接口
- 步骤:定义接口,确保只有一个抽象方法,可选添加
@FunctionalInterface注解。 - 示例:
@FunctionalInterface
public interface CustomCalculator {
int calculate(int a, int b); // 唯一抽象方法
}
// 使用Lambda
CustomCalculator adder = (a, b) -> a + b;
System.out.println(adder.calculate(5, 3)); // 输出:8
6. 应用场景
- Stream API:
map()、filter()、reduce()等方法接受函数式接口作为参数。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.filter(name -> name.length() > 3) // Predicate
.map(name -> name.toUpperCase()) // Function
.forEach(System.out::println); // Consumer
- 并发编程:
CompletableFuture、并行流(parallelStream())中使用函数式接口处理异步任务。 - 事件监听:GUI编程中事件处理(如按钮点击)。
- 依赖注入:Spring框架中的
@FunctionalInterface用于定义函数式Bean(如Function<Request, Response>)。
7. 注意事项与限制
- 抽象方法唯一性:若接口有多个抽象方法,无法作为函数式接口使用。
- 类型推断:Lambda的参数类型可省略,由编译器推断。
- 作用域:Lambda可访问外部变量(需为
final或等效final)。 - 异常处理:函数式接口的抽象方法可声明抛出异常,但需与Lambda体中的异常兼容。
- 性能:Lambda表达式在运行时可能生成内部类实例,但JIT编译器会优化热代码路径。
- 与匿名内部类对比:Lambda更简洁,但匿名内部类可访问局部变量的
final变量(Lambda仅需等效final)。
8. 高级用法
- 方法引用:简化Lambda表达式,直接引用现有方法(如
System.out::println)。 - 组合函数:通过
and()、or()、negate()等默认方法组合Predicate实例。 - 自定义函数式接口的扩展:结合默认方法提供额外功能(如缓存、日志)。
总结
函数式接口是Java支持函数式编程的基石,通过Lambda表达式实现代码的简洁性和可读性。它们在标准库(如Stream API)、并发编程和框架中广泛应用。自定义函数式接口需严格遵守抽象方法唯一性规则,并合理利用默认方法和静态方法增强功能。理解函数式接口的规则和应用场景,有助于编写更高效、更现代的Java代码。



1213

被折叠的 条评论
为什么被折叠?



