← 返回题目列表

Java 内置的函数式接口有哪些?Function、Consumer、Supplier、Predicate 怎么区分?

高频 中等 第 6 / 24 题 更新于 2026/08/03
函数式接口FunctionConsumerSupplierPredicate

简化版

**Java 8 在 java.util.function 包里内置了一批「函数式接口」,它们是 Lambda 表达式的「类型」——写一个 Lambda,本质是在实现某个函数式接口。**四大核心接口按「有没有入参、有没有返回值」区分:Function<T,R>——有入参 T、有返回值 R(「转换」:输入一个、输出另一个),核心方法 R apply(T)Consumer<T>——有入参 T、无返回值(「消费」:拿到就用掉,如打印),void accept(T)Supplier<T>——无入参、有返回值 T(「供给/生产」:凭空产出一个,如工厂、延迟计算),T get()Predicate<T>——有入参 T、返回 boolean(「判断」:满不满足条件,如过滤),boolean test(T)还有变体BiFunction/BiConsumer(两个入参)、UnaryOperator(输入输出同类型的 Function)、BinaryOperator(两个同类型入参、同类型返回,如求和)、以及避免装箱的 IntFunction/ToIntFunction 等。核心记忆:看「入参 + 返回值」——有入有出是 Function、有入无出是 Consumer、无入有出是 Supplier、有入出 boolean 是 Predicate。

详细版

四大核心函数式接口

接口抽象方法入参返回语义典型用途
Function<T,R>R apply(T)TR转换map
Consumervoid accept(T)T消费forEach
SupplierT get()T供给延迟/工厂
Predicateboolean test(T)Tboolean判断filter

常见变体

接口说明
BiFunction<T,U,R>两个入参的 Function
BiConsumer<T,U>两个入参的 Consumer
UnaryOperatorFunction<T,T>(入出同类型)
BinaryOperatorBiFunction<T,T,T>(两个同类型入参→同类型,如求和)
IntFunction/ToIntFunction/IntPredicate避免装箱的基本类型特化
// Function:转换(T → R)
Function<String, Integer> length = s -> s.length();
Integer len = length.apply("hello");   // 5

// Consumer:消费(T → void)
Consumer<String> print = s -> System.out.println(s);
print.accept("hi");

// Supplier:供给(() → T)
Supplier<List<String>> factory = ArrayList::new;
List<String> list = factory.get();

// Predicate:判断(T → boolean)
Predicate<Integer> isPositive = n -> n > 0;
boolean b = isPositive.test(5);   // true

// 组合(默认方法)
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> both = isPositive.and(isEven);   // 且
Function<String, Integer> f = length.andThen(n -> n * 2);  // 先 length 再 *2

⚠️ 理解这些接口的关键,不是背名字,而是看「入参和返回值的组合」——每个 Lambda 都对应一个函数式接口,你看它「几个输入、有没有输出、输出是不是 boolean」,就知道该用哪个:有输入有输出 → Function;有输入没输出 → Consumer;没输入有输出 → Supplier;有输入输出 boolean → Predicate。这也是为什么 Stream 的方法参数正好对应它们:map(Function)(转换)、forEach(Consumer)(消费)、filter(Predicate)(判断)、Optional.orElseGet(Supplier)(延迟供给)。另外这些接口都有默认方法做组合Predicate.and/or/negateFunction.andThen/composeConsumer.andThen),能把小函数拼成大函数,这是函数式编程「组合」思想的体现。

完整版教学

一、函数式接口是 Lambda 的类型

先理解「Lambda 和函数式接口的关系」:

函数式接口:只有一个抽象方法的接口(Single Abstract Method, SAM)
  可以标 @FunctionalInterface 注解(编译器检查只有一个抽象方法)

Lambda 表达式的本质:实现某个函数式接口
  Runnable r = () -> System.out.println("run");
  → 这个 Lambda 实现了 Runnable 的 run() 方法
  → Lambda 的"类型"就是 Runnable(函数式接口)

所以:
  写一个 Lambda,编译器根据"上下文期望的函数式接口"确定它的类型
  Lambda 的参数/返回值 要匹配那个接口的抽象方法

java.util.function 包:
  Java 8 内置了一批通用的函数式接口
  → 不用为每种"函数形状"自己定义接口
  → Function/Consumer/Supplier/Predicate 等覆盖了常见的函数形状

为什么需要这些内置接口:
  Lambda 需要一个"类型"(接口)来承载
  与其到处自定义接口,不如用内置的通用接口

函数式接口是「只有一个抽象方法的接口」(SAM),可标 @FunctionalInterfaceLambda 的本质是实现某个函数式接口Runnable r = () -> ... 实现了 run(),Lambda 的类型就是 Runnable)。java.util.function 包内置了一批通用函数式接口(Function/Consumer/Supplier/Predicate 等),不用为每种「函数形状」自己定义接口。理解「函数式接口=只有一个抽象方法的接口(SAM)、Lambda 的本质是实现函数式接口、java.util.function 内置通用接口不用自己定义」,就理解了这些接口存在的意义。

二、Function:转换(有入有出)

Function<T,R> 是「转换」——输入一个、输出另一个:

Function<T, R>:
  R apply(T t)   接收 T,返回 R
  语义:把 T 转换成 R

例:
  Function<String, Integer> length = s -> s.length();  // String → Integer
  Function<Integer, Integer> square = n -> n * n;      // Integer → Integer

用在哪:
  Stream.map(Function):把流的每个元素转换成另一个
  → map(s -> s.toUpperCase())  转大写

默认方法(组合):
  andThen:先做本函数,再做后一个
    length.andThen(n -> n * 2)  // 先算长度,再乘 2
  compose:先做参数函数,再做本函数(顺序相反)
    length.compose(f)  // 先做 f,再做 length

特化变体:
  UnaryOperator<T>:Function<T,T>(输入输出同类型,如 n -> n+1)
  BiFunction<T,U,R>:两个入参(如 (a,b) -> a+b)
  ToIntFunction<T>:返回 int(避免装箱)
  IntFunction<R>:入参是 int

Function<T,R> 是「转换」(有入有出,R apply(T))——把 T 转成 R(s -> s.length())。用在 Stream.map(Function)(转换每个元素)。组合默认方法:andThen(先本函数再后一个)、compose(先参数函数再本函数)。变体:UnaryOperator<T>(入出同类型)、BiFunction<T,U,R>(两入参)、ToIntFunction(返回 int 避免装箱)。理解「Function 转换(有入有出,R apply(T))、用于 map、andThen/compose 组合、变体 UnaryOperator(入出同类型)/BiFunction(两入参)」,就掌握了 Function。

三、Consumer:消费(有入无出)

Consumer<T> 是「消费」——拿到就用掉,没有返回:

Consumer<T>:
  void accept(T t)   接收 T,无返回
  语义:消费 T(用它做点事,但不返回结果)

例:
  Consumer<String> print = s -> System.out.println(s);
  Consumer<User> save = user -> repository.save(user);

用在哪:
  Stream.forEach(Consumer):对每个元素做操作(打印、保存)
  Optional.ifPresent(Consumer):有值时消费它
  list.forEach(System.out::println)

默认方法:
  andThen:连续消费(先做本 Consumer,再做后一个)
    print.andThen(log).accept(x)  // 先打印,再记日志

特化变体:
  BiConsumer<T,U>:两个入参(如 Map.forEach((k,v) -> ...))
  IntConsumer:消费 int(避免装箱)

特点:Consumer 有副作用(它就是用来"做事"的,如打印、保存)
  → 和 Function/Predicate 的"纯函数"不同,Consumer 是"执行动作"

Consumer<T> 是「消费」(有入无出,void accept(T))——拿到 T 做点事但不返回(s -> print(s))。用在 Stream.forEach(Consumer)Optional.ifPresent。组合:andThen(连续消费)。变体:BiConsumer<T,U>(两入参,Map.forEach)。特点:Consumer 有副作用(就是用来「做事」的,和 Function/Predicate 的纯函数不同)。理解「Consumer 消费(有入无出,void accept(T))、用于 forEach/ifPresent、有副作用(执行动作)、变体 BiConsumer(Map.forEach)」,就掌握了 Consumer。

四、Supplier:供给(无入有出)

Supplier<T> 是「供给」——凭空产出一个:

Supplier<T>:
  T get()   无入参,返回 T
  语义:供给/生产一个 T(凭空产出)

例:
  Supplier<List<String>> factory = ArrayList::new;  // 生产一个 List
  Supplier<Long> now = System::currentTimeMillis;   // 生产当前时间
  Supplier<String> random = () -> UUID.randomUUID().toString();

用在哪(核心价值:延迟/惰性):
  Optional.orElseGet(Supplier):值不存在时才调 get 生产默认值
    → 相比 orElse(value),orElseGet 是"惰性"的(用到才生产)
  Logger.debug(() -> 复杂计算):只有 debug 开启才计算(延迟求值)
  Stream.generate(Supplier):无限流生成

为什么 Supplier 重要——延迟求值:
  普通传值:orElse(expensiveDefault())  ← 不管用不用都先算好
  用 Supplier:orElseGet(() -> expensiveDefault())  ← 用到才算
  → 避免不必要的计算(惰性、延迟)

特化变体:IntSupplier/BooleanSupplier 等

Supplier<T> 是「供给」(无入有出,T get())——凭空产出一个 T(ArrayList::new 生产 List)。核心价值是延迟/惰性Optional.orElseGet(Supplier)(值不存在才生产默认值,相比 orElse(value) 是惰性的)、Logger.debug(() -> 复杂计算)(只有 debug 开启才计算)、Stream.generate延迟求值避免不必要的计算orElse 不管用不用都先算、orElseGet 用到才算)。理解「Supplier 供给(无入有出,T get())、核心价值延迟/惰性(orElseGet 用到才生产、Logger 延迟计算)、避免不必要的计算」,就掌握了 Supplier。

五、Predicate:判断(有入出 boolean)

Predicate<T> 是「判断」——满不满足条件:

Predicate<T>:
  boolean test(T t)   接收 T,返回 boolean
  语义:判断 T 是否满足某条件

例:
  Predicate<Integer> isPositive = n -> n > 0;
  Predicate<String> isEmpty = String::isEmpty;

用在哪:
  Stream.filter(Predicate):过滤(保留满足条件的)
  Stream.anyMatch/allMatch/noneMatch(Predicate):匹配判断
  removeIf(Predicate):移除满足条件的

默认方法(逻辑组合,很实用):
  and:两个条件都满足   isPositive.and(isEven)
  or:满足其一          isPositive.or(isZero)
  negate:取反          isPositive.negate()
  → 把多个简单判断组合成复杂判断

静态方法:
  Predicate.isEqual(x):判断是否等于 x
  Predicate.not(pred):取反(Java 11+)

特化变体:IntPredicate/LongPredicate(避免装箱)
BiPredicate<T,U>:两个入参的判断

Predicate<T> 是「判断」(有入出 boolean,boolean test(T))——判断是否满足条件(n -> n > 0)。用在 Stream.filter(Predicate)(过滤)、anyMatch/allMatchremoveIf逻辑组合默认方法很实用and(都满足)、or(满足其一)、negate(取反),把简单判断组合成复杂判断。变体:BiPredicate<T,U>(两入参)。理解「Predicate 判断(有入出 boolean,boolean test(T))、用于 filter/anyMatch/removeIf、and/or/negate 逻辑组合、变体 BiPredicate」,就掌握了 Predicate。

六、区分方法与组合思想

总结「怎么区分」和「组合思想」:

区分方法(看入参和返回值):
              有返回值        无返回值
  有入参    Function<T,R>    Consumer<T>
            (返回boolean → Predicate<T>)
  无入参    Supplier<T>      Runnable(无入无出)

  → 有入有出=Function、有入无出=Consumer、
    无入有出=Supplier、有入出boolean=Predicate、无入无出=Runnable

对应 Stream 方法(记忆锚点):
  map(Function)       转换
  filter(Predicate)   过滤
  forEach(Consumer)   消费
  orElseGet(Supplier) 惰性供给

组合思想(默认方法):
  Function.andThen/compose:函数串联
  Predicate.and/or/negate:条件逻辑组合
  Consumer.andThen:消费串联
  → 把小的、单一职责的函数,组合成复杂的函数
  → 这是函数式编程"组合优于继承"的体现

常见变体总结:
  Bi 前缀:两个入参(BiFunction/BiConsumer/BiPredicate)
  UnaryOperator/BinaryOperator:入出同类型的 Function/BiFunction
  Int/Long/Double 前缀:基本类型特化(避免装箱,提性能)

区分方法看「入参 + 返回值」:有入有出=Function、有入无出=Consumer、无入有出=Supplier、有入出 boolean=Predicate、无入无出=Runnable。对应 Stream 方法(记忆锚点):map(Function)/filter(Predicate)/forEach(Consumer)/orElseGet(Supplier)组合思想:默认方法(Function.andThen/Predicate.and-or-negate/Consumer.andThen)把小函数组合成复杂函数。变体Bi 前缀(两入参)、UnaryOperator/BinaryOperator(入出同类型)、Int/Long/Double 前缀(基本类型特化避免装箱)。理解「区分看入参+返回值(有入有出 Function/有入无出 Consumer/无入有出 Supplier/有入出 boolean Predicate)、对应 Stream 方法、组合默认方法、变体 Bi/UnaryOperator/基本类型特化」,就掌握了区分和组合的全貌。

记忆钩子:「Java 内置函数式接口(java.util.function),Lambda 的类型;四大核心看入参+返回值:①Function<T,R>(有入有出,R apply(T),转换,用于 map,andThen/compose 组合)②Consumer(有入无出,void accept(T),消费/有副作用,用于 forEach)③Supplier(无入有出,T get(),供给/延迟惰性,用于 orElseGet 用到才生产)④Predicate(有入出 boolean,boolean test(T),判断,用于 filter,and/or/negate 组合);区分:有入有出=Function、有入无出=Consumer、无入有出=Supplier、有入出 boolean=Predicate;变体:Bi 前缀(两入参)、UnaryOperator/BinaryOperator(入出同类型)、Int/Long/Double 前缀(避免装箱)」

七、常见误区与追问

  • 误区:这些函数式接口要死记名字。 关键是看「入参和返回值的组合」:有输入有输出→Function、有输入没输出→Consumer、没输入有输出→Supplier、有输入输出 boolean→Predicate、无输入无输出→Runnable;理解了组合就不用背名字。
  • 误区:Consumer 和 Function 差不多。 Function 有返回值(转换 T→R,纯函数);Consumer 无返回值(消费 T,有副作用、执行动作如打印/保存);一个是「转换出新值」、一个是「拿值做事不返回」。
  • 误区:orElse 和 orElseGet 一样。 orElse(value) 不管用不用都会先计算 value;orElseGet(Supplier) 是惰性的,只有 Optional 为空时才调 Supplier 生产默认值;如果默认值计算昂贵,orElseGet 避免了不必要的计算——这是 Supplier 延迟求值的价值。
  • 误区:Predicate 只能表达单一条件。 Predicate 有 and/or/negate 默认方法——可以把多个简单 Predicate 组合成复杂条件:isPositive.and(isEven)(都满足)、a.or(b)(满足其一)、p.negate()(取反);这是函数式「组合」思想的体现。
  • 追问:为什么有 IntFunction、ToIntFunction 这些基本类型特化接口? 为了避免自动装箱/拆箱的性能开销——Function<Integer, Integer> 会把 int 装箱成 Integer(有开销),而 IntUnaryOperator、ToIntFunction、IntPredicate 等直接用基本类型 int,避免装箱,在大量数据处理(如 IntStream)时性能更好。
  • 追问:Function 的 andThen 和 compose 有什么区别? andThen 是「先做本函数、再做参数函数」:f.andThen(g) 等于 x -> g(f(x));compose 是「先做参数函数、再做本函数」:f.compose(g) 等于 x -> f(g(x));顺序相反,andThen 是正向串联、compose 是反向。
  • 追问:UnaryOperator 和 Function 是什么关系? UnaryOperator 继承自 Function<T,T>——它是「输入和输出同类型」的 Function 的特例(如 n -> n+1 是 UnaryOperator);同理 BinaryOperator 是 BiFunction<T,T,T> 的特例(两个同类型入参、同类型返回,如 Integer::sum);用它们语义更明确(表示「同类型的运算」)。

八、加强记忆

Java 8 在 java.util.function 内置了一批「函数式接口」——它们是 Lambda 的「类型」(函数式接口 = 只有一个抽象方法 SAM,Lambda 本质是实现它)。四大核心,按「入参 + 返回值」区分Function<T,R>(有入有出,R apply(T)转换,用于 mapandThen/compose 组合);Consumer<T>(有入无出,void accept(T)消费/有副作用,用于 forEach);Supplier<T>(无入有出,T get()供给/延迟惰性,用于 orElseGet——值不存在才生产、避免不必要计算);Predicate<T>(有入出 boolean,boolean test(T)判断,用于 filterand/or/negate 逻辑组合)。区分口诀:有入有出=Function、有入无出=Consumer、无入有出=Supplier、有入出 boolean=Predicate、无入无出=Runnable。对应 Stream 方法map(Function)/forEach(Consumer)/orElseGet(Supplier)/filter(Predicate)变体Bi 前缀(两入参,BiFunction/BiConsumer/BiPredicate)、UnaryOperator<T>Function<T,T> 入出同类型)、BinaryOperator<T>(两同类型入参→同类型,如求和)、Int/Long/Double 前缀(基本类型特化避免装箱)。这些接口都有默认方法做组合,体现函数式「组合」思想。一句话「内置函数式接口是 Lambda 的类型,看入参+返回值区分:Function(有入有出/转换/map)、Consumer(有入无出/消费/forEach)、Supplier(无入有出/延迟供给/orElseGet)、Predicate(有入出 boolean/判断/filter);变体 Bi(两入参)/UnaryOperator(入出同类型)/基本类型特化(避免装箱);默认方法组合小函数」。