Appearance
2026-08-29 · Lambda、函数式接口、方法引用——把"行为"本身当作参数传来传去的写法。
Java 函数式编程
1. 函数式接口:只有一个抽象方法的接口
函数式接口(Functional Interface):里面只有一个抽象方法的接口。
Runnable 就是最典型的:只有一个 run() 方法。
java
@FunctionalInterface // 注解:声明这是函数式接口(多写一个抽象方法会编译报错)
public interface Runnable {
void run();
}为什么只能有一个抽象方法? 因为 Lambda 就是给这一个方法"填空"的——方法多了,Lambda 就不知道该实现谁了。
@FunctionalInterface 不是必须写的,但建议写:编译器帮你检查"到底是不是函数式接口"。
2. Lambda 快速回顾
Lambda = 函数式接口的匿名实现简写:
java
// 匿名内部类(完整写法)
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("跑");
}
};
// Lambda(简写:参数 -> 方法体)
Runnable r2 = () -> System.out.println("跑");语法规则:(参数列表) -> { 方法体 }
- 参数类型可省略(靠接口定义推断)
- 单参数可省括号:
s -> s.length() - 单行方法体可省大括号和 return:
(a, b) -> a - b
java
// 带参数的例子
Comparator<String> c = (a, b) -> a.compareTo(b);Lambda 的本质:把"一段行为"封装成对象传递。以前只能传数据(int、String),现在能把"逻辑"当参数传。
3. 四大内置函数式接口(java.util.function)
日常写代码 90% 用的就是这四个。记住:接口的泛型 = 参数类型,返回值看方法名。
① Consumer<T>:消费(有进无出)
java
// 方法:void accept(T t) —— 吃进一个,什么都不返回
Consumer<String> printer = s -> System.out.println(s);
printer.accept("你好"); // 你好用途:遍历时"对每个元素做点事"。
list.forEach(consumer)就是消费。
② Supplier<T>:生产(无进有出)
java
// 方法:T get() —— 不接收参数,吐出一个
Supplier<String> s = () -> "产出的字符串";
System.out.println(s.get()); // 产出的字符串用途:按需生成数据(默认值、随机数、惰性加载)。
③ Function<T, R>:转换(进一个出一个)
java
// 方法:R apply(T t) —— 输入一个类型,输出另一个类型
Function<String, Integer> len = s -> s.length();
System.out.println(len.apply("hello")); // 5(String → Integer)用途:类型转换/映射。T 是入参类型,R 是返回类型。
④ Predicate<T>:判断(返回布尔)
java
// 方法:boolean test(T t) —— 给一个条件,返回 true/false
Predicate<String> isLong = s -> s.length() > 5;
System.out.println(isLong.test("hello")); // false
System.out.println(isLong.test("hello!!!") ); // true用途:过滤条件。
list.stream().filter(predicate)里全是它。
四个口诀:
| 接口 | 方法 | 方向 | 场景 |
|---|---|---|---|
Consumer<T> | accept(t) | 进 → 无 | 对每个元素做操作(forEach) |
Supplier<T> | get() | 无 → 出 | 提供值 |
Function<T,R> | apply(t) | 进 → 出 | 转换(map) |
Predicate<T> | test(t) | 进 → 布尔 | 判断(filter) |
还有两个"加餐"变体:
BiConsumer<A,B>(两个参数)、BiFunction<A,B,R>(两个入参)。见名知义,需要时现查即可。
4. 方法引用::——Lambda 的极致简写
Lambda 经常会遇到"就调一个现成方法"的情况,这时可以直接写 方法引用(::),更短:
java
// Lambda
Consumer<String> c1 = s -> System.out.println(s);
// 方法引用(System.out 里的 println 方法)
Consumer<String> c2 = System.out::println;
// Lambda
Function<String, Integer> f1 = s -> s.length();
// 方法引用
Function<String, Integer> f2 = String::length;三种常用形式:
| 形式 | 写法 | 示例 |
|---|---|---|
| 静态方法 | 类名::静态方法 | Math::max |
| 实例方法(已有对象) | 对象::方法 | System.out::println |
| 实例方法(参数是对象) | 类名::实例方法 | String::length、String::toLowerCase |
java
// 实际场景:list.forEach 里最常用
list.forEach(System.out::println); // 每个元素打印一行
list.stream().map(String::toUpperCase) // 每个转大写翻译规则:
::左边是"谁的方法",右边是"方法名"。先写完整 Lambda,再压缩成::,不用背。
5. 本篇小结
- 函数式接口 = 只有一个抽象方法,
@FunctionalInterface校验 - Lambda = 给那个唯一方法填空,把"行为"当参数传
- 四大接口背口诀:Consumer 消费、Supplier 生产、Function 转换、Predicate 判断
- 方法引用
::是 Lambda 的简写,碰到"调用现成方法"就用 - 这四个接口是下一章 Stream 的地基——Stream 每个操作符接收的都是它们