Java Lambda表达式

Lambda 表达式是 Java 8 引入的重要特性,它基于函数式接口,允许你将功能视为方法参数,或将代码视为数据。以下是 Lambda 表达式从基础到高阶的详细解析:

1. 基础概念与语法

Lambda 表达式本质是匿名函数,它没有名称,但有参数列表、函数主体、返回类型,可能还有异常列表。

基本语法

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

关键点

  • 参数列表:可省略参数类型;若只有一个参数,括号可省略。
  • 箭头符号->,分隔参数和主体。
  • 主体:若为单个表达式,自动返回值;若为代码块,则需显式return语句。

2. 函数式接口

Lambda 表达式依赖函数式接口(Functional Interface):

  • 仅含一个抽象方法的接口(可含默认方法、静态方法)。
  • 需用@FunctionalInterface注解(可选,但推荐)。

示例接口

@FunctionalInterface
interface MyFunction {
    int apply(int a, int b); // 唯一抽象方法
}

3. Lambda 表达式的基础应用

3.1 无参数、无返回值
Runnable runnable = () -> System.out.println("Hello Lambda!");
3.2 单参数、单表达式
Consumer<String> printer = s -> System.out.println(s);
3.3 多参数、多语句
BiFunction<Integer, Integer, Integer> sum = (a, b) -> {
    int result = a + b;
    return result;
};
3.4 异常处理
@FunctionalInterface
interface CheckedExceptionHandler {
    void handle() throws IOException; // 声明异常
}

// Lambda中抛出异常
CheckedExceptionHandler handler = () -> {
    throw new IOException("Error occurred");
};

4. 方法引用与构造器引用

4.1 静态方法引用
// 原Lambda
Function<String, Integer> parser = s -> Integer.parseInt(s);

// 方法引用
Function<String, Integer> parser = Integer::parseInt;
4.2 实例方法引用
// 原Lambda
Predicate<String> isEmpty = s -> s.isEmpty();

// 方法引用
Predicate<String> isEmpty = String::isEmpty;
4.3 构造器引用
// 原Lambda
Supplier<List<String>> listCreator = () -> new ArrayList<>();

// 构造器引用
Supplier<List<String>> listCreator = ArrayList::new;

5. 变量捕获(闭包)

Lambda 可捕获外部的final 或 effectively final变量(即赋值后未再修改的变量)。

示例

int offset = 10;
UnaryOperator<Integer> adder = x -> x + offset; // 捕获offset
System.out.println(adder.apply(5)); // 输出15

注意

  • 捕获的变量不可修改,否则编译错误。
  • 局部内部类、匿名类也有类似机制,但 Lambda 更简洁。

6. 高阶函数与复合 Lambda

6.1 高阶函数

接收函数式接口作为参数或返回函数式接口的方法。

示例

// 定义高阶函数
static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
    List<T> result = new ArrayList<>();
    for (T item : list) {
        if (predicate.test(item)) {
            result.add(item);
        }
    }
    return result;
}

// 使用Lambda调用
List<String> filtered = filter(
    Arrays.asList("apple", "banana", "cherry"),
    s -> s.startsWith("a")
);
6.2 复合 Lambda

通过函数式接口的默认方法组合多个 Lambda。

示例

// Predicate组合
Predicate<String> startsWithA = s -> s.startsWith("a");
Predicate<String> endsWithB = s -> s.endsWith("b");
Predicate<String> combined = startsWithA.and(endsWithB);

// Function组合
Function<Integer, Integer> add5 = x -> x + 5;
Function<Integer, Integer> multiply2 = x -> x * 2;
Function<Integer, Integer> add5ThenMultiply2 = add5.andThen(multiply2);

7. Stream API 与 Lambda

Java 8 的 Stream API 大量使用 Lambda 表达式,用于集合的过滤、映射、归约等操作。

示例

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                .filter(n -> n % 2 == 0) // 过滤偶数
                .map(n -> n * 2)        // 每个数乘以2
                .reduce(0, Integer::sum); // 求和
System.out.println(sum); // 输出12 (2*2 + 4*2)

8. 高级特性

8.1 类型推断

编译器可根据上下文推断 Lambda 的参数类型:

// 无需显式声明参数类型
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
8.2 泛型函数式接口

自定义泛型函数式接口:

@FunctionalInterface
interface MyMapper<T, R> {
    R map(T value);
}

// 使用
MyMapper<String, Integer> lengthMapper = s -> s.length();
8.3 与匿名类的区别
  • this 引用:Lambda 中的this指向外部类实例,匿名类中的this指向匿名类自身。
  • 编译方式:Lambda 通过 invokedynamic 指令实现,匿名类会生成独立的.class 文件。

9. 最佳实践

  1. 保持简洁:避免复杂的 Lambda(超过 3 行逻辑建议使用方法引用或具名方法)。
  2. 明确命名:函数式接口和参数名应表意清晰。
  3. 避免副作用:Lambda 应是纯函数,不修改外部状态。
  4. 优先使用标准接口:如FunctionConsumerPredicate等,减少自定义接口。

总结

Lambda 表达式让 Java 更具函数式编程风格,通过简洁的语法和函数式接口,大幅提升代码可读性和生产力。结合 Stream API,它成为处理集合数据的强大工具。掌握 Lambda 是现代 Java 开发的必备技能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值