Java 函数式编程

引言

Java 8 引入的 Lambda 表达式和函数式接口,让 Java 具备了函数式编程的能力。函数式编程不是要替代面向对象,而是提供了一种更简洁、更具表达力的编程范式,特别适合集合操作、事件处理和异步编程。


一、Lambda 表达式

1.1 语法

java 复制代码
// 完整语法
(Type1 param1, Type2 param2) -> { expression; return value; }

// 简化规则
// 1. 类型推断:参数类型可省略
(a, b) -> a + b

// 2. 单参数:括号可省略
name -> System.out.println(name)

// 3. 单表达式:花括号和 return 可省略
(a, b) -> a + b

// 4. 多行语句:需要花括号和 return
(a, b) -> {
    int sum = a + b;
    return sum;
}

1.2 常见用法

java 复制代码
// 替代匿名内部类
// Before
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
};

// After
Runnable r2 = () -> System.out.println("hello");

// Comparator
Comparator<String> c1 = (a, b) -> a.length() - b.length();

// 自定义接口
@FunctionalInterface
interface StringProcessor {
    String process(String input);
}
StringProcessor toUpper = s -> s.toUpperCase();

1.3 变量捕获

java 复制代码
// Lambda 可以捕获 effectively final 的局部变量
String prefix = "Hello, "; // effectively final

Function<String, String> greeter = name -> prefix + name;
greeter.apply("Alice"); // "Hello, Alice"

// Lambda 内部不能修改捕获的变量
// prefix = "Hi, "; // 编译错误

二、函数式接口

2.1 @FunctionalInterface

java 复制代码
// 函数式接口:只包含一个抽象方法的接口
@FunctionalInterface
public interface Converter<T, R> {
    R convert(T input);
}

// 默认方法和静态方法不影响
@FunctionalInterface
public interface SmartConverter<T, R> {
    R convert(T input);

    default R convertOrDefault(T input, R defaultValue) {
        return input == null ? defaultValue : convert(input);
    }

    static <T> SmartConverter<T, T> identity() {
        return t -> t;
    }
}

2.2 JDK 内置函数式接口

接口 参数 返回 用途 示例
Supplier<T> T 提供值 () -> new Config()
Consumer<T> T void 消费值 s -> log.info(s)
BiConsumer<T,U> T,U void 消费两个值 (k,v) -> map.put(k,v)
Function<T,R> T R 转换 s -> s.length()
BiFunction<T,U,R> T,U R 双参数转换 (a,b) -> a + b
UnaryOperator<T> T T 一元操作 s -> s.trim()
BinaryOperator<T> T,T T 二元操作 (a,b) -> a + b
Predicate<T> T boolean 判断 s -> s.isEmpty()
BiPredicate<T,U> T,U boolean 双参数判断 (a,b) -> a.equals(b)

基本类型特化(避免装箱拆箱):

java 复制代码
IntSupplier supplier = () -> 42;           // int -> int
IntFunction<String> func = i -> "N" + i;   // int -> String
IntPredicate pred = i -> i > 0;            // int -> boolean
IntConsumer consumer = i -> log(i);         // int -> void
IntUnaryOperator op = i -> i * 2;          // int -> int
IntBinaryOperator binOp = (a, b) -> a + b; // int,int -> int

三、方法引用

3.1 四种形式

形式 语法 等价 Lambda
静态方法引用 ClassName::staticMethod a -> ClassName.staticMethod(a)
实例方法引用 instance::method a -> instance.method(a)
类型方法引用 ClassName::method (obj, a) -> obj.method(a)
构造器引用 ClassName::new () -> new ClassName()

3.2 示例

java 复制代码
// 1. 静态方法引用
Function<String, Integer> parser = Integer::parseInt;

// 2. 实例方法引用
String prefix = "Hello, ";
Function<String, String> greeter = prefix::concat;

// 3. 类型方法引用(最常用)
Function<String, Integer> lengthFn = String::length;
BiPredicate<String, String> equalsFn = String::equals;

// 4. 构造器引用
Supplier<List<String>> listFactory = ArrayList::new;
Function<Integer, int[]> arrayFactory = int[]::new;

3.3 方法引用 vs Lambda

java 复制代码
// 优先使用方法引用,更简洁
list.forEach(System.out::println);        // 方法引用
list.forEach(s -> System.out.println(s)); // Lambda,稍冗长

// 方法引用无法表达时使用 Lambda
list.stream().map(s -> s + "!");          // Lambda

四、函数组合

4.1 Function 组合

java 复制代码
Function<String, String> trim = String::trim;
Function<String, String> toUpper = String::toUpperCase;
Function<String, String> addPrefix = s -> "Hello, " + s;

// andThen:先执行当前,再执行参数
Function<String, String> pipeline = trim.andThen(toUpper).andThen(addPrefix);
String result = pipeline.apply("  alice  "); // "Hello, ALICE"

// compose:先执行参数,再执行当前
Function<String, String> pipeline2 = addPrefix.compose(toUpper).compose(trim);
String result2 = pipeline2.apply("  alice  "); // "Hello, ALICE"

4.2 Predicate 组合

java 复制代码
Predicate<String> nonEmpty = s -> !s.isEmpty();
Predicate<String> shortStr = s -> s.length() < 10;
Predicate<String> startsWithA = s -> s.startsWith("A");

// and / or / negate
Predicate<String> combined = nonEmpty.and(shortStr).and(startsWithA.negate());

combined.test("Hello");  // true
combined.test("ABCDEF"); // false

4.3 Consumer 组合

java 复制代码
Consumer<String> log = s -> System.out.println("LOG: " + s);
Consumer<String> audit = s -> System.out.println("AUDIT: " + s);

Consumer<String> logAndAudit = log.andThen(audit);
logAndAudit.accept("user login");
// LOG: user login
// AUDIT: user login

五、高阶函数实战

5.1 策略模式

java 复制代码
// 传统策略模式需要多个类
// 函数式策略模式:用 Map + Lambda 替代
Map<String, Function<Double, Double>> strategies = Map.of(
    "regular", p -> p * 0.95,
    "vip", p -> p * 0.8,
    "svip", p -> p * 0.7
);

double discounted = strategies.getOrDefault(userLevel, p -> p).apply(originalPrice);

5.2 装饰器模式

java 复制代码
// 函数式装饰器
Function<String, String> base = s -> s;

Function<Function<String, String>, Function<String, String>> trimDecorator =
    fn -> s -> fn.apply(s.trim());

Function<Function<String, String>, Function<String, String>> upperDecorator =
    fn -> s -> fn.apply(s.toUpperCase());

Function<Function<String, String>, Function<String, String>> prefixDecorator =
    fn -> s -> fn.apply(">> " + s);

Function<String, String> decorated = trimDecorator
    .andThen(upperDecorator)
    .andThen(prefixDecorator)
    .apply(base);

decorated.apply("  hello  "); // ">> HELLO"

5.3 惰性求值

java 复制代码
public class Lazy<T> {
    private Supplier<T> supplier;
    private T value;
    private boolean computed = false;

    public Lazy(Supplier<T> supplier) {
        this.supplier = supplier;
    }

    public T get() {
        if (!computed) {
            value = supplier.get();
            computed = true;
            supplier = null;
        }
        return value;
    }

    public <R> Lazy<R> map(Function<T, R> fn) {
        return new Lazy<>(() -> fn.apply(this.get()));
    }

    public <R> Lazy<R> flatMap(Function<T, Lazy<R>> fn) {
        return new Lazy<>(() -> fn.apply(this.get()).get());
    }
}

// 使用
Lazy<String> lazy = new Lazy<>(() -> {
    System.out.println("Computing...");
    return "hello";
});
// 此时未计算

String result = lazy.get(); // "Computing..." -> "hello"
String again = lazy.get(); // 直接返回缓存值,不再计算

5.4 回调模式

java 复制代码
public <T> void executeAsync(Supplier<T> task, Consumer<T> onSuccess, Consumer<Exception> onError) {
    new Thread(() -> {
        try {
            T result = task.get();
            onSuccess.accept(result);
        } catch (Exception e) {
            onError.accept(e);
        }
    }).start();
}

// 使用
executeAsync(
    () -> fetchData(),
    result -> System.out.println("Success: " + result),
    error -> System.err.println("Error: " + error.getMessage())
);

六、常见陷阱

6.1 Lambda 中的 this

java 复制代码
public class Demo {
    private String name = "Outer";

    public void test() {
        // Lambda 中的 this 指向外部类实例
        Runnable r = () -> System.out.println(this.name); // "Outer"

        // 匿名内部类中的 this 指向匿名类实例
        Runnable r2 = new Runnable() {
            private String name = "Inner";
            public void run() {
                System.out.println(this.name); // "Inner"
            }
        };
    }
}

6.2 Lambda 与受检异常

java 复制代码
// Lambda 中受检异常必须处理
list.forEach(s -> {
    try {
        Files.readString(Path.of(s));
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
});

// 封装工具方法
private static <T> Supplier<T> unchecked(Callable<T> callable) {
    return () -> {
        try {
            return callable.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

6.3 Lambda 与重载歧义

java 复制代码
// 编译错误:Lambda 类型推断歧义
execute(() -> {}); // 是 Runnable 还是 Callable<Void>?

// 显式指定类型
execute((Runnable) () -> {});
execute((Callable<Void>) () -> null);

总结

要点 建议
Lambda 优先替代匿名内部类
方法引用 比 Lambda 更简洁时使用
函数式接口 优先使用 JDK 内置接口
函数组合 andThen/compose 构建处理管道
变量捕获 effectively final,不要 hack
受检异常 Lambda 中必须 try-catch 或包装