第63章

Java Lambda表达式

掌握函数式编程基础、方法引用、Stream API和实际应用案例

学习目标

Lambda表达式基础

Lambda表达式是Java 8引入的重要特性,它允许我们以更简洁的方式表示匿名函数。Lambda表达式的核心思想是将函数作为一等公民,支持函数式编程范式。

基本语法

语法格式:
// 基本格式
(parameters) -> expression

// 或者
(parameters) -> { statements; }

// 示例
(x, y) -> x + y
() -> System.out.println("Hello")
x -> x * 2
  • 参数列表:可以为空或包含多个参数
  • 箭头符号:-> 分隔参数和表达式
  • 表达式体:可以是单个表达式或代码块
  • 类型推断:编译器自动推断参数类型

替代匿名内部类

对比示例:
// 传统匿名内部类
Runnable oldWay = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
};

// Lambda表达式
Runnable newWay = () -> System.out.println("Hello World");
  • 代码更简洁,可读性更强
  • 减少样板代码
  • 编译器优化更好
  • 支持类型推断

函数式接口

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

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

int result1 = add.calculate(5, 3);      // 8
int result2 = multiply.calculate(5, 3); // 15
  • 只能有一个抽象方法
  • 可以有默认方法和静态方法
  • @FunctionalInterface注解可选但推荐
  • Lambda表达式的目标类型

常用函数式接口

Java 8在java.util.function包中提供了许多预定义的函数式接口,这些接口涵盖了大部分常见的使用场景。

接口名称 方法签名 用途 示例
Predicate<T> boolean test(T t) 断言型接口,用于条件判断 x -> x > 0
Function<T,R> R apply(T t) 函数型接口,用于类型转换 s -> s.length()
Consumer<T> void accept(T t) 消费型接口,用于处理数据 s -> System.out.println(s)
Supplier<T> T get() 供给型接口,用于生产数据 () -> Math.random()
UnaryOperator<T> T apply(T t) 一元操作符,输入输出同类型 x -> x * x
BinaryOperator<T> T apply(T t1, T t2) 二元操作符,两个输入一个输出 (a, b) -> a + b

方法引用详解

方法引用是Lambda表达式的简化形式,当Lambda表达式只是调用一个已存在的方法时,可以使用方法引用来进一步简化代码。

四种方法引用类型

  • 静态方法引用:ClassName::staticMethod
  • 实例方法引用:ClassName::instanceMethod
  • 特定对象的实例方法引用:object::instanceMethod
  • 构造器引用:ClassName::new
方法引用示例:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Lambda表达式
names.forEach(name -> System.out.println(name));

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

// 静态方法引用
List<Integer> numbers = Arrays.asList("1", "2", "3")
    .stream()
    .map(Integer::parseInt)  // 等价于 s -> Integer.parseInt(s)
    .collect(Collectors.toList());

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

Stream API应用

Stream API是Java 8的另一个重要特性,它与Lambda表达式完美结合,提供了强大的数据处理能力。Stream支持函数式编程风格的操作,如过滤、映射、归约等。

Stream API综合示例:
List<Person> people = Arrays.asList(
    new Person("Alice", 25, "Engineer"),
    new Person("Bob", 30, "Manager"),
    new Person("Charlie", 35, "Engineer"),
    new Person("David", 28, "Designer")
);

// 过滤、映射、收集
List<String> engineerNames = people.stream()
    .filter(p -> "Engineer".equals(p.getJob()))  // 过滤工程师
    .map(Person::getName)                        // 提取姓名
    .collect(Collectors.toList());               // 收集结果

// 分组统计
Map<String, Long> jobCounts = people.stream()
    .collect(Collectors.groupingBy(
        Person::getJob,
        Collectors.counting()
    ));

// 查找最年长的人
Optional<Person> oldest = people.stream()
    .max(Comparator.comparing(Person::getAge));

// 计算平均年龄
Double averageAge = people.stream()
    .collect(Collectors.averagingInt(Person::getAge));

Lambda表达式最佳实践

推荐做法

  • 保持Lambda表达式简短,通常不超过3行
  • 使用方法引用替代简单的Lambda表达式
  • 为复杂的Lambda表达式提取为方法
  • 优先使用标准函数式接口
  • 避免在Lambda中修改外部变量
  • 使用并行流时注意线程安全

避免做法

  • 不要在Lambda中使用过多的嵌套
  • 避免在Lambda中抛出检查异常
  • 不要滥用并行流,考虑性能开销
  • 避免在Lambda中进行复杂的业务逻辑
  • 不要忽略Stream的惰性求值特性
  • 避免重复创建相同的Lambda表达式

章节总结