Lambda 与函数式接口
更新时间:2026-09-20。Lambda 大量配合 Stream 与 Optional 使用;接口与默认方法见 接口与抽象类。
一、Lambda 本质:一个函数式接口的实例
Java 没有顶级函数类型,-> 语法必须「兑现」成某个只含一个抽象方法的接口,这叫函数式接口:
@FunctionalInterface
interface Converter<F, T> { T convert(F from); }
Converter<String, Integer> c = s -> Integer.parseInt(s);
Integer n = c.convert("42");@FunctionalInterface 非必需,但写上后编译器会校验「确实只有一个抽象方法」(default/static 方法不算)。
二、四个核心内置函数式接口
java.util.function 下最常用的四个:
| 接口 | 抽象方法 | 含义 | 示例 |
|---|---|---|---|
Function<T,R> | R apply(T) | 一进一出,转换 | s -> s.length() |
Predicate<T> | boolean test(T) | 判断真假 | s -> s.isEmpty() |
Consumer<T> | void accept(T) | 消费、无返回 | s -> System.out.println(s) |
Supplier<T> | T get() | 无中生有、提供值 | () -> new ArrayList<>() |
还有双参数的 BiFunction、BiPredicate,以及避免自动装箱的原始类型特化:IntFunction<R>、ToIntFunction<T>、IntPredicate、LongConsumer 等。热路径上用特化接口避免装箱开销。
三、目标类型推断
Lambda 的类型由它被赋给/传入的位置(目标类型)决定,所以同一个 x -> x + 1 在不同上下文可代表不同接口。方法参数是函数式接口时可直接传 lambda:
list.stream().filter(s -> s.length() > 3).map(String::toUpperCase);
// Predicate<String> Function<String,String>四、为什么要求 effectively final
Lambda(和匿名类)引用外部局部变量时,该变量必须 final 或「事实上不可再变(effectively final)」:
int base = 10;
Function<Integer, Integer> f = x -> x + base; // OK
// base++; // 一旦再赋值就编译失败原因:lambda 捕获的是变量值的快照,若允许之后再改,栈帧已销毁后 lambda 仍可能执行,会造成「看到的是哪个值」的语义混乱;Java 干脆用不可变约束消除它。实例字段不受此限(捕获的是 this)。
五、方法引用:lambda 的简写
当 lambda 只是「调一个现成方法」,可用 :::
| 形式 | 例子 | 等价 lambda |
|---|---|---|
| 静态方法 | Integer::parseInt | s -> Integer.parseInt(s) |
| 实例方法(特定对象) | System.out::println | x -> System.out.println(x) |
| 实例方法(类名,参数当接收者) | String::length | s -> s.length() |
| 构造方法 | ArrayList::new | () -> new ArrayList<>() |
六、与匿名内部类的区别
- 写法更短,编译器对非捕获 lambda 倾向于用
invokedynamic懒生成、复用单例,而不是每次new一个内部类文件(经典匿名内部类会生成Outer$1.class); - lambda 里的
this指向外围类实例,匿名内部类的this指向那个匿名对象——这是常见坑; - lambda 不能有自己的状态字段,需要多方法/字段时仍得用普通类或匿名类。
七、实战位置
行为参数化是 Lambda 的主战场:Comparator.comparing(...)、Stream 的 filter/map/reduce、Optional 的 map/orElseGet、线程池的 execute(() -> ...)、CompletableFuture 回调等,都是在传函数式接口。
总结
Java 的 Lambda 不是函数而是单抽象方法接口(函数式接口)的实例,靠目标类型推断匹配;记住四大接口——Function 转换、Predicate 判断、Consumer 消费、Supplier 供给,热路径用原始类型特化避免装箱;闭包只能捕获 effectively final 局部变量,因为捕获的是值快照;方法引用 类::方法 / 对象::方法 / 类型::new 是直接转发的简写;lambda 的 this 指外围类且非捕获实例常被复用,与每次新建对象、this 指向自身的匿名内部类不同。