Skip to content

一、何为函数式编程?

函数式编程(Functional Programming,简称FP)是一种编程范式,它将计算视为数学函数的求值,并避免使用可变状态和可变数据。Java函数式编程基于Lambda表达式,在Java中,核心思想是将函数作为一等公民:

  • 函数是一等公民:函数可以作为参数传递、作为返回值、赋值给变量。
  • 不可变性:避免修改状态,强调使用不可变对象
  • 声明式编程:关注"做什么"而不是"如何做"
  • 无副作用:函数执行不影响外部状态

二、Lambda表达式

Lambda表达式是java8引入的一种简洁的语法形式,用于表示匿名函数。核心思想是将代码块作为方法参数传递,从而简化代码并提高可读性。 它可以作为参数传递给方法或函数接口,并且可以在需要函数式编程特性的地方使用。Lambda表达式仅适用于函数式接口(只有一个抽象方法的接口),可直接实现该接口的实例,避免编写传统匿名内部类。Lambda表达式是一种匿名函数,可以用来表示一个函数式接口的实现。
基本语法格式:(参数列表)->表达式或代码块

  • 参数列表: 可以显示或隐式指定参数类型,只有一个参数时还可以省略括号。
  • 箭头符号:->,表示参数和函数体之间的分隔符
  • 函数体: 可以是一个表达式或代码块 具体形式如下:
java
// 标准格式
(参数列表) -> { 表达式体 }

// 简化格式
(参数列表) -> 表达式

// 无参数
() -> { 表达式体 }

// 单参数可省略括号
参数 -> 表达式

用法示例:

java
// 1. 无参数
() -> System.out.println("Hello");

// 2. 一个参数
(x) -> x * 2;
x -> x * 2;  // 单个参数时可省略括号

// 3. 多个参数
(int x, int y) -> x + y; //参数类型显示指定
(x, y) -> x + y;  // 参数类型可隐式推断

// 4. 多行代码
(x, y) -> {
    int sum = x + y;
    return sum * 2;
};

使用限制:

  • 单方法接口:Lambda表达式只能用于实现一个抽象方法的接口。
  • 捕获变量:只能捕获有效最终变量。
  • 可读性:如果逻辑复杂,可能会降低代码可读性。

实际开发中的应用:

  • stream api
  • 事件处理
  • 函数式接口

三、函数式接口

函数式接口是只有一个抽象方法的接口,可以使用@FunctionalInterface注解标记。

1.内置函数式接口

  • 1.Predicate< T > - 断言接口(接收T返回boolean)
java
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

// 示例
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true

// 组合使用
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isPositiveAndEven = isPositive.and(isEven);

// 集合过滤
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evens = numbers.stream()
    .filter(isEven)
    .collect(Collectors.toList());
  • 2.Supplier< T > - 供应者接口(无参数返回T)
java
@FunctionalInterface
public interface Supplier< T > {
    T get();
}

// 示例
Supplier<Double> randomSupplier = () -> Math.random();
Supplier<String> nameSupplier = () -> "Default";

// 延迟初始化
Supplier<ExpensiveObject> lazyInit = () -> {
    System.out.println("Creating expensive object...");
    return new ExpensiveObject();
};

// 需要时才创建
ExpensiveObject obj = lazyInit.get();
  • 3.Consumer< T > - 消费者接口(接收T无返回值)
java
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

// 示例
Consumer<String> printer = s -> System.out.println(s);
Consumer<String> logger = s -> log(s);

// andThen组合
Consumer<String> printAndLog = printer.andThen(logger);
printAndLog.accept("Hello");
  • 4. Function< T, R > - 函数接口(接收T返回R)
java
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

// 示例
Function<String, Integer> lengthFunc = s -> s.length();
Function<Integer, String> toStringFunc = i -> String.valueOf(i);

// compose和andThen组合
Function<String, String> functionChain = lengthFunc.andThen(toStringFunc);

// 映射示例
List<String> names = Arrays.asList("Tom", "Jerry");
List<Integer> lengths = names.stream()
    .map(lengthFunc)
    .collect(Collectors.toList());
  • 5.其他常用函数式接口
java
// UnaryOperator< T > - 一元操作符(Function的特例) 接收T返回T(Function的特殊情况)
UnaryOperator<Integer> square = x -> x * x;

// BinaryOperator<T> - 二元操作符 接收两个T返回T
BinaryOperator<Integer> add = (a, b) -> a + b;

// BiFunction<T, U, R> - 二元函数
BiFunction<Integer, Integer, String> adder = (a, b) -> String.valueOf(a + b);

// BiPredicate<T, U> - 二元断言
BiPredicate<String, Integer> lengthCheck = (s, len) -> s.length() > len;

// BiConsumer<T, U> - 二元消费者
BiConsumer<String, Integer> printer = (name, age) -> 
    System.out.println(name + ": " + age);

2.自定义函数式接口

java
@FunctionalInterface
interface StringProcessor {
    String process(String input);
    
    // 可以有默认方法
    default StringProcessor andThen(StringProcessor after) {
        return input -> after.process(this.process(input));
    }
    
    // 可以有静态方法
    static StringProcessor toUpperCase() {
        return String::toUpperCase;
    }
}

// 使用
StringProcessor trimmer = s -> s.trim();
StringProcessor upperCaser = StringProcessor.toUpperCase();
StringProcessor pipeline = trimmer.andThen(upperCaser);
System.out.println(pipeline.process("  hello  ")); // HELLO

四、Lambda表达式与函数式接口的关系

Lambda表达式是Java实现函数式编程的基础,它提供了简洁的函数定义语法,使得函数可以作为参数传递,从而支持高阶函数等函数式编程特性。Lambda表达式必须与函数式接口结合使用,函数式接口是一个只包含一个抽象方法的接口,用@FunctionInterface注解标注。

五、方法引用

方法引用是Lambda的简化写法,有以下四种形式:

1.静态方法引用

  • 语法:
    类名::静态方法
  • 示例:
java
// Lambda形式
Function<Integer, String> converter1 = i -> String.valueOf(i);

// 方法引用形式
Function<Integer, String> converter2 = String::valueOf;

2.任意对象实例方法引用

  • 语法:
  • 类名::实例方法
  • 示例:
java
// Lambda形式
Consumer<String> printer1 = s -> System.out.println(s);

// 方法引用形式
Consumer<String> printer2 = System.out::println;

// 集合排序示例
List<String> names = Arrays.asList("Alice", "Bob");
names.sort(String::compareToIgnoreCase);

3.特定对象的实例方法引用

  • 语法:
    对象::实例方法
  • 示例:
java
String prefix = "Mr. ";
Function<String, String> adder = s -> prefix + s;
// 等价于
Function<String, String> adder = prefix::concat;

4.构造方法引用

  • 语法:
    类名::new
  • 示例:
java
// Lambda形式
Supplier<List<String>> supplier1 = () -> new ArrayList<>();

// 方法引用形式
Supplier<List<String>> supplier2 = ArrayList::new;

// 带参数的构造方法
Function<Integer, int[]> arrayCreator = int[]::new;
int[] arr = arrayCreator.apply(10); // 创建长度为10的数组

Released under the MIT License.