Java中的函数式编程随着Lambda表达式和函数式接口(从Java 8开始)的出现而发展。
问题历史
在Java 8之前,所有接口都是抽象方法的集合,这种范式是面向对象的。引入函数式接口和Lambda表达式后,有可能编写更简洁的代码并遵循FP原则,从而提高了代码的可读性和表达性。
问题
与事件处理、集合或异步逻辑相关的代码变得冗长:必须创建单独的类或匿名内部类。这使得代码的维护和扩展变得困难。
解决方案
函数式接口是仅具有一个抽象方法的接口。它可以作为Lambda表达式的目标类型。在标准库中出现了常见的函数式接口类型,例如Function<T, R>、Predicate<T>、Supplier<T>、Consumer<T>,以及创建自定义接口的能力。
代码示例:
import java.util.function.Function; public class FunctionalExample { public static void main(String[] args) { // 标准函数式接口Function Function<String, Integer> stringLength = s -> s.length(); System.out.println(stringLength.apply("Java")); // 输出4 } }
主要特点:
可以将函数式接口声明为具有多个抽象方法,如果其他方法具有默认实现或是静态方法吗?
不可以,函数式接口只能包含一个抽象方法。默认方法(default)或静态方法可以存在。
可以从另一个具有多个抽象方法的接口继承函数式接口吗?
不可以,如果最终接口拥有多个抽象方法,则该接口不再是函数式的,不能用于Lambda表达式。
Java中的函数式接口与其他语言(例如C#)中的SAM接口有何不同?
在Java中,没有直接的关键字来声明SAM接口,直到出现了@FunctionalInterface注释。与C#不同的是,C#中的delegate明确设置了签名,而Java只需一个抽象方法和可选注释即可供编译器使用。
@FunctionalInterface注释——这会使编译器在接口不再是函数式时不立即报错。在一个大型项目中,决定普遍使用Lambda,包括在处理与商业逻辑没有直接关系的数据实体的地方。结果,复杂逻辑变得难以追踪,Lambda掩盖了代码意图,调试变得困难。
优点:
缺点:
在另一个项目中,仔细分析哪些接口适合FP。使用了Predicate、Function和自定义接口来处理集合和事件。对于实体和数据存储,没有使用FP。
优点:
缺点: