Skip to content

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::lengthString::toLowerCase
java
// 实际场景:list.forEach 里最常用
list.forEach(System.out::println);   // 每个元素打印一行

list.stream().map(String::toUpperCase)  // 每个转大写

翻译规则:: 左边是"谁的方法",右边是"方法名"。先写完整 Lambda,再压缩成 ::,不用背。

5. 本篇小结

  1. 函数式接口 = 只有一个抽象方法,@FunctionalInterface 校验
  2. Lambda = 给那个唯一方法填空,把"行为"当参数传
  3. 四大接口背口诀:Consumer 消费、Supplier 生产、Function 转换、Predicate 判断
  4. 方法引用 :: 是 Lambda 的简写,碰到"调用现成方法"就用
  5. 这四个接口是下一章 Stream 的地基——Stream 每个操作符接收的都是它们