Java 函数式编程指南

27 阅读3分钟

Java 函数式编程是在 Java 8 中引入的重要特性,它使得编写更简洁、可读性更高的代码成为可能。下面是一个全面的介绍:

1. 核心概念

Lambda 表达式

// 传统方式
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda 表达式
Runnable r2 = () -> System.out.println("Hello");

// 带参数的 Lambda
Comparator<String> comp = (s1, s2) -> s1.compareTo(s2);

函数式接口

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

// 使用
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;

System.out.println(add.calculate(5, 3));      // 8
System.out.println(multiply.calculate(5, 3)); // 15

2. Java 内置的函数式接口

常用接口

import java.util.function.*;

// Predicate - 接受参数返回boolean
Predicate<String> isEmpty = s -> s.isEmpty();

// Function - 接受参数返回结果
Function<String, Integer> stringLength = s -> s.length();

// Consumer - 接受参数无返回
Consumer<String> printer = s -> System.out.println(s);

// Supplier - 无参数返回结果
Supplier<Double> randomSupplier = () -> Math.random();

// UnaryOperator - 输入输出类型相同
UnaryOperator<String> toUpper = s -> s.toUpperCase();

// BinaryOperator - 两个相同类型的参数返回同类型结果
BinaryOperator<Integer> adder = (a, b) -> a + b;

3. Stream API

基本操作

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// 过滤和映射
List<String> result = names.stream()
    .filter(name -> name.length() > 4)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// [ALICE, CHARLIE, DAVID]

// 排序
List<String> sorted = names.stream()
    .sorted()
    .collect(Collectors.toList());

// 去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> distinct = numbers.stream()
    .distinct()
    .collect(Collectors.toList());
// [1, 2, 3]

终端操作

List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);

// 统计
long count = nums.stream().count();
int sum = nums.stream().mapToInt(Integer::intValue).sum();
Optional<Integer> max = nums.stream().max(Integer::compare);

// 匹配
boolean anyMatch = nums.stream().anyMatch(n -> n > 3); // true
boolean allMatch = nums.stream().allMatch(n -> n > 0); // true
boolean noneMatch = nums.stream().noneMatch(n -> n < 0); // true

// 归约
int total = nums.stream().reduce(0, (a, b) -> a + b);
int product = nums.stream().reduce(1, (a, b) -> a * b);

4. 方法引用

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 静态方法引用
names.forEach(System.out::println);

// 实例方法引用
names.stream()
    .map(String::toUpperCase)  // 等价于 s -> s.toUpperCase()
    .forEach(System.out::println);

// 构造函数引用
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> newList = listSupplier.get();

5. Optional 类

public class OptionalExample {
    public static void main(String[] args) {
        // 创建 Optional
        Optional<String> optional = Optional.of("Hello");
        Optional<String> emptyOptional = Optional.empty();
        Optional<String> nullableOptional = Optional.ofNullable(null);
        
        // 使用
        optional.ifPresent(System.out::println); // 如果存在则打印
        
        String result = optional.orElse("Default");
        String result2 = emptyOptional.orElseGet(() -> "Generated Default");
        
        // 链式调用
        Optional<String> processed = optional
            .filter(s -> s.length() > 3)
            .map(String::toUpperCase);
            
        // flatMap 用于嵌套 Optional
        Optional<Optional<String>> nested = optional.map(Optional::of);
        Optional<String> flattened = optional.flatMap(Optional::of);
    }
}

6. 实际应用示例

示例1:数据处理

public class Employee {
    private String name;
    private int age;
    private double salary;
    private String department;
    
    // 构造器、getter、setter
}

public class FunctionalExample {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Alice", 30, 50000, "IT"),
            new Employee("Bob", 25, 45000, "HR"),
            new Employee("Charlie", 35, 60000, "IT"),
            new Employee("David", 28, 48000, "HR")
        );
        
        // 按部门分组,计算平均工资
        Map<String, Double> avgSalaryByDept = employees.stream()
            .collect(Collectors.groupingBy(
                Employee::getDepartment,
                Collectors.averagingDouble(Employee::getSalary)
            ));
        
        // 找出IT部门年龄大于25的员工
        List<Employee> itSeniors = employees.stream()
            .filter(e -> "IT".equals(e.getDepartment()))
            .filter(e -> e.getAge() > 25)
            .sorted(Comparator.comparing(Employee::getSalary).reversed())
            .collect(Collectors.toList());
        
        // 并行处理
        double totalSalary = employees.parallelStream()
            .mapToDouble(Employee::getSalary)
            .sum();
    }
}

示例2:自定义函数式接口

@FunctionalInterface
interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}

public class CustomFunctional {
    public static void main(String[] args) {
        TriFunction<Integer, Integer, Integer, Integer> sumThree = 
            (a, b, c) -> a + b + c;
        
        System.out.println(sumThree.apply(1, 2, 3)); // 6
        
        // 函数组合
        Function<Integer, Integer> add2 = x -> x + 2;
        Function<Integer, Integer> multiply3 = x -> x * 3;
        
        Function<Integer, Integer> composed = add2.andThen(multiply3);
        System.out.println(composed.apply(5)); // (5+2)*3 = 21
    }
}

7. 最佳实践和注意事项

优点:

  1. 简洁性:减少样板代码
  2. 可读性:表达更清晰
  3. 并行处理:更容易实现并行计算
  4. 延迟计算:提高性能

注意事项:

  1. 不要过度使用:保持代码可读性
  2. 避免副作用:函数式代码应该是无状态的
  3. 性能考虑:Stream 创建有开销,小数据集可能传统循环更快
  4. 调试困难:Lambda 表达式调试相对困难

何时使用:

  • 集合处理和数据转换
  • 事件处理
  • 回调函数
  • 并行处理
  • 条件逻辑的简化

8. Java 9+ 增强

Java 9 及后续版本增加了更多函数式特性:

// Java 9: Stream API 增强
List.of(1, 2, 3, 4, 5).stream()
    .takeWhile(n -> n < 4)  // 取满足条件的元素
    .forEach(System.out::println); // 1, 2, 3

// Java 10: var 与 Lambda
var list = List.of("a", "b", "c");
list.forEach((var s) -> System.out.println(s));

Java 函数式编程通过 Stream API、Lambda 表达式和函数式接口,大大提高了代码的表达能力和简洁性,是现代 Java 开发中必须掌握的重要技能。