Java Lambda 表达式学习笔记

一、什么是 Lambda 表达式?

Lambda 表达式是 Java 8 引入的核心特性,本质是匿名函数(没有名称的函数),用于简化「只有一个抽象方法的接口」(函数式接口)的实现。

  • 核心作用:替代匿名内部类,让代码更简洁、更易读。
  • 核心思想:强调「做什么」,而非「怎么做」(函数式编程思想)。

1.1 先看对比:匿名内部类 vs Lambda 表达式

Runnable 接口(只有一个 run() 抽象方法)为例:

// 1. 传统匿名内部类
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("匿名内部类执行");
    }
};

// 2. Lambda 表达式(简化后)
Runnable runnable2 = () -> System.out.println("Lambda 表达式执行");

可见:Lambda 表达式去掉了冗余的模板代码,只保留了核心的执行逻辑。

二、Lambda 表达式的核心语法

2.1 语法结构

Lambda 表达式的基本格式

(参数列表) -> { 方法体 }

各部分说明:

组成部分说明
(参数列表)可选:参数类型可省略(编译器自动推断);单个参数可省略括号;无参数用 ()
->必选:分隔符,连接参数列表和方法体
{ 方法体 }可选:单行代码可省略 {};;有返回值时单行代码可省略 return

2.2 语法示例(以 Comparator 接口为例)

Comparator<Integer> 接口只有一个 compare(T o1, T o2) 抽象方法,用于比较两个值:

import java.util.Comparator;

public class LambdaSyntaxDemo {
    public static void main(String[] args) {
        // 1. 完整写法(参数类型+括号+方法体+return+{})
        Comparator<Integer> comparator1 = (Integer a, Integer b) -> {
            return a - b;
        };

        // 2. 简化:省略参数类型(编译器自动推断)
        Comparator<Integer> comparator2 = (a, b) -> {
            return a - b;
        };

        // 3. 简化:单行方法体省略 {} 和 return
        Comparator<Integer> comparator3 = (a, b) -> a - b;

        // 4. 测试
        System.out.println(comparator3.compare(5, 3)); // 输出:2
    }
}

2.3 不同场景的语法简化

场景Lambda 写法示例说明
无参数、无返回值() -> System.out.println("Hello")无参数用 ()
单个参数、无返回值s -> System.out.println(s)单个参数可省略括号
多个参数、有返回值(a, b) -> a + b单行返回省略 return
多行方法体(a, b) -> { int sum = a + b; return sum; }多行必须加 {}return

三、函数式接口(Lambda 的前提)

Lambda 表达式只能用于函数式接口,这是核心前提!

3.1 什么是函数式接口?

  • 定义:只有一个抽象方法的接口(可以有默认方法、静态方法)。
  • 标识:可通过 @FunctionalInterface 注解标记(可选,但推荐,编译器会检查是否符合函数式接口规范)。

3.2 常见内置函数式接口(Java 8 提供)

Java 8 在 java.util.function 包下提供了大量常用函数式接口,无需自定义:

接口名抽象方法用途示例
Consumer<T>void accept(T t)消费一个参数,无返回值(s) -> System.out.println(s)
Supplier<T>T get()无参数,返回一个值() -> new ArrayList<>()
Function<T, R>R apply(T t)接收 T 类型,返回 R 类型(s) -> s.length()
Predicate<T>boolean test(T t)接收 T 类型,返回布尔值(n) -> n > 0
示例:使用内置函数式接口
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // 1. Consumer:消费字符串
        Consumer<String> printConsumer = s -> System.out.println("消费:" + s);
        printConsumer.accept("Hello Lambda"); // 输出:消费:Hello Lambda

        // 2. Supplier:生成随机数
        Supplier<Double> randomSupplier = () -> Math.random();
        System.out.println("生成随机数:" + randomSupplier.get()); // 输出:生成随机数:0.xxxx

        // 3. Function:将字符串转为长度
        Function<String, Integer> lengthFunction = s -> s.length();
        System.out.println("字符串长度:" + lengthFunction.apply("Java")); // 输出:字符串长度:4

        // 4. Predicate:判断是否为偶数
        Predicate<Integer> evenPredicate = n -> n % 2 == 0;
        System.out.println("是否为偶数:" + evenPredicate.test(6)); // 输出:是否为偶数:true
    }
}

3.3 自定义函数式接口

如果内置接口不满足需求,可自定义函数式接口:

// 自定义函数式接口(@FunctionalInterface 注解可选,但推荐)
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b); // 唯一抽象方法

    // 允许有默认方法(Java 8 新增)
    default void printTip() {
        System.out.println("这是一个计算器接口");
    }

    // 允许有静态方法
    static int add(int a, int b) {
        return a + b;
    }
}

// 使用
public class CustomFunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Lambda 实现加法
        Calculator addCalculator = (a, b) -> a + b;
        System.out.println(addCalculator.calculate(3, 5)); // 输出:8

        // Lambda 实现乘法
        Calculator multiplyCalculator = (a, b) -> a * b;
        System.out.println(multiplyCalculator.calculate(3, 5)); // 输出:15

        // 调用默认方法
        addCalculator.printTip(); // 输出:这是一个计算器接口
    }
}

四、Lambda 表达式的常用场景

4.1 集合遍历(forEach)

Java 8 为 Collection 接口新增了 forEach(Consumer) 方法,结合 Lambda 简化遍历:

import java.util.ArrayList;
import java.util.List;

public class LambdaForEachDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Python");
        list.add("Go");

        // 传统 for 循环
        for (String s : list) {
            System.out.println(s);
        }

        // Lambda + forEach
        list.forEach(s -> System.out.println(s));

        // 更简化:方法引用(后续补充)
        list.forEach(System.out::println);
    }
}

4.2 集合排序(sort)

简化 Collections.sort()List.sort() 的排序逻辑:

import java.util.ArrayList;
import java.util.List;

public class LambdaSortDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Banana");
        list.add("Apple");
        list.add("Cherry");

        // 传统排序(匿名内部类)
        /*
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareTo(s2);
            }
        });
        */

        // Lambda 排序(按字符串自然顺序)
        list.sort((s1, s2) -> s1.compareTo(s2));

        // 输出排序结果
        list.forEach(System.out::println); // Apple, Banana, Cherry
    }
}

4.3 线程创建(Runnable)

简化线程创建的代码:

public class LambdaThreadDemo {
    public static void main(String[] args) {
        // 传统匿名内部类创建线程
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1执行");
            }
        });

        // Lambda 创建线程
        Thread thread2 = new Thread(() -> System.out.println("线程2执行"));

        thread1.start();
        thread2.start();
    }
}

五、Lambda 表达式的高级特性

5.1 变量捕获

Lambda 表达式可以访问外部变量,但有严格限制:

  1. 可以访问静态变量(全局);
  2. 可以访问成员变量(当前对象的);
  3. 可以访问局部变量,但局部变量必须是 final 或「事实上的 final」(即没有被重新赋值)。
示例:变量捕获
public class LambdaVariableDemo {
    // 静态变量
    private static String staticVar = "静态变量";
    // 成员变量
    private String memberVar = "成员变量";

    public void test() {
        // 局部变量(事实上的 final,未重新赋值)
        String localVar = "局部变量";

        // Lambda 访问各类变量
        Runnable runnable = () -> {
            System.out.println(staticVar);    // 允许
            System.out.println(memberVar);    // 允许
            System.out.println(localVar);     // 允许
            // localVar = "修改局部变量"; // 报错:局部变量必须是 final 或事实上的 final
        };
        runnable.run();
    }

    public static void main(String[] args) {
        new LambdaVariableDemo().test();
    }
}

5.2 方法引用(Lambda 的简化版)

当 Lambda 表达式的逻辑只是「调用一个已存在的方法」时,可使用方法引用进一步简化,格式为 类名/对象名::方法名

常见方法引用类型
类型示例等价的 Lambda 表达式
静态方法引用Integer::parseInts -> Integer.parseInt(s)
实例方法引用System.out::printlns -> System.out.println(s)
对象方法引用String::lengths -> s.length()
构造方法引用ArrayList::new() -> new ArrayList<>()
示例:方法引用
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

public class MethodReferenceDemo {
    public static void main(String[] args) {
        // 1. 静态方法引用:Integer.parseInt(String)
        Function<String, Integer> function1 = Integer::parseInt;
        System.out.println(function1.apply("123")); // 输出:123

        // 2. 实例方法引用:System.out.println(String)
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.forEach(System.out::println); // 等价于 s -> System.out.println(s)

        // 3. 构造方法引用:ArrayList::new
        Supplier<List<String>> supplier = ArrayList::new;
        List<String> newList = supplier.get();
        newList.add("C");
        System.out.println(newList); // 输出:[C]
    }
}

六、Lambda 表达式的优缺点

优点

  1. 代码简洁:去掉匿名内部类的冗余模板,核心逻辑更突出;
  2. 函数式编程:强调「做什么」,符合现代编程思想;
  3. 便于并行处理:结合 Stream API(Java 8 新增)可轻松实现集合的并行操作。

缺点

  1. 可读性:过于简化的 Lambda 可能让新手难以理解;
  2. 调试困难:匿名函数没有名称,调试时栈轨迹不直观;
  3. 适用场景有限:仅能用于函数式接口,无法替代多方法接口的实现。

总结

  1. 核心前提:Lambda 表达式仅适用于函数式接口(只有一个抽象方法的接口),@FunctionalInterface 注解可标记并校验。
  2. 核心语法(参数列表) -> { 方法体 },支持多维度简化(省略参数类型、括号、{}、return)。
  3. 核心价值:简化匿名内部类的写法,结合 Stream API、集合遍历 / 排序等场景能大幅提升代码简洁性;方法引用是 Lambda 的进一步简化形式。
  4. 注意事项:Lambda 访问局部变量时,变量需是 final 或「事实上的 final」;调试难度略高,需适度使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值