函数式编程(Functional Programming)是一种编程范式,它将计算视为数学函数的求值,强调使用纯函数和不可变数据。
除了函数式编程之外, 还有 命令式编程,声明式编程 等编程范式。
在函数式编程中,函数是一等公民,它可以作为参数传递给其他函数,也可以作为返回值返回。函数式编程避免了副作用(Side Effects)和共享状态,这样可以提高代码的可维护性、可测试性和并发性。
以下是函数式编程的一些主要特征:
函数式编程最重要的特征是 纯函数 和 函数是一等公民
函数式编程不是一个特定语言的概念,而是一种编程风格或哲学。虽然一些编程语言(如Haskell、Scala、Clojure)更天然地支持函数式编程,但在越来越多的编程语言中,如Java、JavaScript、Python,也引入了函数式编程的特性。函数式编程的思想对于处理并发、提高代码可维护性和表达力都有很大的帮助。
纯函数是指相同的输入总会得到相同的输出,并且不会产生副作用的函数。纯函数的两个特点:
举个例子,
public class Person{
private int a = 1;
public int add(int b){
return a + b;
}
public int pow(int c){
return c * c;
}
public static void main(String[] args){
Person p = new Person();
p.add(1);
p.pow(2);
}
}
上面代码中add(int b)这个方法就不符合函数式编程,这个函数调用后的结果不确定,它的结果不仅取决于b还取决于字段a。而pow(int c)这函数就是符合函数式编程的典范,只要调用它,输入的值c确定了返回值就肯定确定了。
在编程语言中,将函数视为一等公民(First-Class Citizen)意味着函数具有以下特性:
在支持函数作为一等公民的编程语言中,函数与其他数据类型(如整数、字符串等)具有相同的地位,可以像操作其他数据一样灵活地使用。这种特性是函数式编程风格的核心之一。
举个简单的例子,假设有一个接受两个整数和一个函数作为参数的函数 operate
:
@FunctionalInterface
public interface IntBinaryOperator {
int applyAsInt(int left, int right);
// 其他默认方法和静态方法...
}
public class FunctionAsFirstClassCitizenExample {
// 函数作为参数
public static int operate(int a, int b, IntBinaryOperator operator) {
return operator.applyAsInt(a, b);
}
// 函数作为返回值
public static IntBinaryOperator getOperator(String operation) {
switch (operation) {
case "+":
return (x, y) -> x + y;
case "-":
return (x, y) -> x - y;
case "*":
return (x, y) -> x * y;
default:
throw new IllegalArgumentException("Unsupported operation: " + operation);
}
}
public static void main(String[] args) {
// 函数赋值给变量
IntBinaryOperator addition = (x, y) -> x + y;
// 函数作为参数传递
int result = operate(5, 3, addition);
System.out.println("Result of addition: " + result);
// 函数作为返回值
IntBinaryOperator selectedOperator = getOperator("*");
// 调用返回的函数
int result2 = operate(4, 2, selectedOperator);
System.out.println("Result of multiplication: " + result2);
}
}
在这个例子中,IntBinaryOperator
是 Java 中的一个函数式接口,用于表示接受两个整数参数并返回一个整数结果的操作。它定义了一个抽象方法 applyAsInt(int left, int right)
,用于执行具体的整数操作。函数 operate
接受两个整数和一个函数作为参数,函数 getOperator
返回一个函数。。这展示了函数作为一等公民的特性,可以方便地传递和组合。
Java 8 引入了对函数式编程的支持,主要通过以下几个特性来实现:
Lambda 表达式: Lambda 表达式是 Java 8 中引入的一个重要特性,它使得能够以更为简洁的方式表示匿名函数。Lambda 表达式可以用来实现函数式接口(只包含一个抽象方法的接口)。
javaCopy code// 旧的方式使用匿名内部类
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};
// 使用 Lambda 表达式
Runnable runnable2 = () -> System.out.println("Hello, World!");
函数式接口: 函数式接口是一个只包含一个抽象方法的接口。Java 8 引入了 @FunctionalInterface
注解来标识函数式接口,以便编译器检查。常见的函数式接口包括 Runnable
、Callable
、Comparator
等。
javaCopy code@FunctionalInterface
interface MyFunction {
void apply();
}
Stream API: Stream API 提供了一种对集合进行声明式操作的方式,支持函数式编程风格的数据处理。它允许对集合进行过滤、映射、归约等操作,而不需要显式使用循环。
javaCopy codeList words = Arrays.asList("apple", "banana", "orange");
// 使用 Stream 进行操作
long count = words.stream()
.filter(word -> word.length() > 5)
.count();
默认方法和静态方法: 接口中引入的默认方法和静态方法使得在接口中引入新方法变得更为灵活。这对于在函数式编程中向接口添加新功能非常有用,而不会破坏现有实现。
javaCopy codeinterface MyInterface {
default void defaultMethod() {
System.out.println("Default method");
}
static void staticMethod() {
System.out.println("Static method");
}
void abstractMethod();
}
Optional 类: Optional
类是 Java 8 引入的一个用于处理可能为 null 的值的容器类。它通过一系列的函数式方法来支持更安全、更函数式的处理空值的方式。
javaCopy codeOptional name = Optional.ofNullable(getName());
String result = name.orElse("Unknown");
这些特性使得 Java 在某种程度上支持函数式编程范式,尽管 Java 仍然是一门面向对象编程的语言。除了以上特性,一些第三方库,如Guava和Vavr,提供了更多函数式编程支持的工具和数据类型。在 Java 9、Java 10 和 Java 11 中,也进一步增强了函数式编程的支持,例如引入了模块化系统和新的集合操作方法。
什么是「函数式编程」? - 知乎 (zhihu.com)
详解Java中的三种函数式编程,以及具体的实现方法 | w3cschool笔记