一、Lambda 表达式的基础 1. 什么是 Lambda?
Lambda 表达式是 Java 8 引入的一种 匿名函数 (没有名称的函数),用于简化 函数式接口 (Functional Interface)的实现。它的核心目标是让代码更简洁,尤其是在处理函数式编程和集合操作时。
2. 为什么需要 Lambda?
替代匿名内部类:传统方式实现接口需要大量样板代码。
函数式编程:支持将函数作为参数传递,或作为返回值。
代码简洁:用更少的代码表达相同的逻辑。
二、Lambda 的语法详解 1. 基本语法结构
参数列表 :与接口中抽象方法的参数一致。
箭头符号 ->:分隔参数和 Lambda 主体。
代码体 :可以是单行代码,也可以是多行代码块。
2. 简化规则
场景
简化写法
原始写法
参数类型可推断
(a, b) -> a + b
(int a, int b) -> { return a + b; }
单个参数
x -> x * x
(x) -> { return x * x; }
无返回值的单行代码
s -> System.out.println(s)
s -> { System.out.println(s); }
单行代码有返回值
(a, b) -> a.compareTo(b)
(a, b) -> { return a.compareTo(b); }
三、函数式接口(Functional Interface) 1. 定义 函数式接口是 有且仅有一个抽象方法 的接口(允许有默认方法),可以用 @FunctionalInterface 注解标记(非强制,但推荐用于编译检查)。
2. 常见内置函数式接口 Java 8 在 java.util.function 包中预定义了四大核心函数式接口:
接口
方法
用途
Supplier<T>
T get()
生产者:提供数据(无输入,有输出)
Consumer<T>
void accept(T t)
消费者:处理数据(有输入,无输出)
Function<T, R>
R apply(T t)
转换器:输入 T,返回 R
Predicate<T>
boolean test(T t)
断言:判断 T 是否满足条件
其他扩展接口(如 BiFunction , UnaryOperator 等)也基于这四大核心。
3.基本使用 3.1、Supplier 生产者
无输入,有输出,用于生成或提供数据
1 2 3 4 5 6 7 8 9 10 11 12 13 Supplier<Double> randomSupplier = Math::random; System.out.println(randomSupplier.get()); Supplier<LocalDateTime> timeSupplier = LocalDateTime::now; System.out.println(timeSupplier.get()); Optional<String> empty = Optional.empty(); String value = empty.orElseGet(()-> "default" );System.out.println(value);
3.2、Consumer 消费者
有输入,无输出,用于消费数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Consumer<String> printConsumer = System.out::println; printConsumer.accept("hello" ); List<String> list = new ArrayList <>(); Consumer<String> addToList = list::add; addToList.accept("java" ); Consumer<String> c1 = s -> System.out.print("姓名: " ); Consumer<String> c2 = s -> System.out.println(s.toUpperCase()); c1.andThen(c2).accept("tom" );
3.3、Function<T, R> 转换器
输入 T,返回 R,用于数据转换
1 2 3 4 5 6 7 8 9 10 11 12 13 Function<String, Integer> lengthFunc = String::length; System.out.println(lengthFunc.apply("hello" )); Function<String, LocalDate> dateParser = s -> LocalDate.parse(s); LocalDate date = dateParser.apply("2022-10-01" );Function<Integer, Integer> multiply = x -> x * 2 ; Function<Integer, Integer> add = x -> x + 3 ; Function<Integer, Integer> calc = multiply.andThen(add); System.out.println(calc.apply(5 ));
3.4、Predicate 断言
判断条件是否成立 ,返回布尔值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Predicate<String> lengthPredicate = s -> s.length() > 5 ; System.out.println(lengthPredicate.test("hello" )); Predicate<Integer> even = n -> n % 2 == 0 ; Predicate<Integer> greater10 = n -> n > 10 ; System.out.println(even.and(greater10).test(14 )); List<String> words = Arrays.asList("java" , "Python" , "C++" ); List<String> longWords = words.stream().filter(s -> s.length() > 3 ).collect(Collectors.toList()); for (String longWord : longWords) { System.out.print(longWord + " " ); }
四、Lambda 的实际应用 1. 替代匿名内部类 传统方式需要显式实现接口,而 Lambda 直接简化:
1 2 3 4 5 6 7 8 9 10 Runnable r1 = new Runnable () { @Override public void run () { System.out.println("Hello" ); } }; Runnable r2 = () -> System.out.println("Hello" );
2. 集合操作与 Stream API 结合 Stream API,Lambda 可以高效处理集合:
1 2 3 4 5 6 7 8 9 List<Integer> numbers = Arrays.asList(1 , 2 , 3 , 4 , 5 ); List<Integer> result = numbers.stream() .filter(n -> n % 2 == 0 ) .map(n -> n * n) .collect(Collectors.toList()); System.out.println(result);
3. 自定义函数式接口 定义自己的函数式接口,实现灵活逻辑:
1 2 3 4 5 6 7 8 @FunctionalInterface interface StringProcessor { String process (String input) ; } StringProcessor toUpper = s -> s.toUpperCase();System.out.println(toUpper.process("hello" ));
五、Lambda 的底层原理 1. Lambda 的实现机制 Java 编译器会将 Lambda 表达式转换为:
一个 静态方法 (包含 Lambda 的代码逻辑)。
一个 invokedynamic 指令,动态绑定到目标函数式接口。
例如,以下 Lambda:
1 Function<Integer, Integer> square = x -> x * x;
会被编译为类似:
1 2 3 private static Integer lambda$0 (Integer x) { return x * x; }
2. 闭包与变量捕获 Lambda 可以捕获外部的 final 或 effectively final 变量
1 2 3 int base = 10 ;Function<Integer, Integer> adder = x -> x + base;
为什么限制为 final?
保证 Lambda 在不同线程中访问变量时,变量的值不会意外改变,避免并发问题。
六、Lambda 的高级用法 1. 方法引用(Method Reference) 静态方法引用 实例方法引用 特定类型的方法引用 构造方法引用 2. 异常处理 Lambda 中抛出异常时,需在函数式接口方法声明中抛出,或在 Lambda 内部捕获:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @FunctionalInterface interface FileReader { String read (File file) throws IOException; } FileReader reader = file -> new String (Files.readAllBytes(file.toPath()));Consumer<File> safeReader = file -> { try { String content = new String (Files.readAllBytes(file.toPath())); System.out.println(content); } catch (IOException e) { e.printStackTrace(); } };
七、Lambda 的注意事项
避免过度简化:复杂逻辑建议拆分为方法,保持代码可读性。
性能考量:Lambda 在大多数场景下性能与匿名类相当,但在极端性能敏感场景需测试。
调试困难:Lambda 的堆栈跟踪可能不如传统方法清晰。
八、完整示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import java.util.Arrays;import java.util.List;import java.util.function.Predicate;public class LambdaDemo { public static void main (String[] args) { List<String> languages = Arrays.asList("Java" , "Python" , "C++" , "JavaScript" , "Ruby" ); Predicate<String> startsWithJ = lang -> lang.startsWith("J" ); languages.stream() .filter(startsWithJ) .forEach(System.out::println); } }
总结
Lambda 表达式 是 Java 函数式编程的核心,通过简洁的语法替代匿名类。
函数式接口 是 Lambda 的类型基础,理解 Supplier、Consumer、Function、Predicate 是关键。
结合 Stream API 可以高效处理集合数据。
方法引用 和 组合函数 能进一步简化代码