[Java] functional interface 分类

functional interface 分类

Java 中有个特殊的注解 @FunctionalInterfaceJDK 中的一些接口带有这个注解,例如

  • Runnable
  • Function
  • Consumer
  • Supplier
  • Predicate

这些接口有什么区别呢?本文会讨论这个问题。

什么是 functional interface

@FunctionalInterface 的源代码中,可以看到它的 javadoc。 其内容如下图所示 ⬇️

其中有几个要点

  • @FunctionalInterface 用于表明一个接口是 functional interface
  • 一个 functional interface 恰有 一个 抽象方法
  • 如果一个接口恰有一个抽象方法,那么编译器会将其当作 functional interface 来处理(不管这个接口是否被 @FunctionalInterface 注解修饰)

常见 functional interface 的分类

基础分类

JDK 中有一些接口带有 @FunctionalInterface 注解,它们的区别是什么呢? 如上文所述,每个 functional interface 恰有一个抽象方法。 我们从以下两个角度来查看各个接口的特点

  • 抽象方法的参数的个数
  • 抽象方法是否有返回值

例如对 RunnableFunctionConsumerSupplier 这四个接口,我们可以进行如下的分类。

参数个数 有返回值吗 接口 代码 抽象方法
0 java.lang.Runnable Runnable.java void run()
0 java.util.function.Supplier<T> Supplier.java T get()
1 java.util.function.Consumer<T> Consumer.java void accept(T t)
1 java.util.function.Function<T, R> Function.java R apply(T t)

如果忽略掉抽象方法的名称的差异,上表中的 4 个接口的差异仅仅在于参数个数以及是否有返回值。

可以简单将它们概括如下 (例如 Runnable 中的抽象方法的入参是 0 个,没有返回值)⬇️

  • <math xmlns="http://www.w3.org/1998/Math/MathML"> R u n n a b l e : ( ) → v o i d Runnable: () \rightarrow void </math>Runnable:()→void
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> S u p p l i e r : ( ) → T Supplier: () \rightarrow T </math>Supplier:()→T
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> C o n s u m e r : T → v o i d Consumer: T \rightarrow void </math>Consumer:T→void
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> F u n c t i o n : T → R Function: T \rightarrow R </math>Function:T→R

用于支持原始类型的 functional interface

在此基础上,可以再看其他的 functional interface。 对 Predicate.java 而言,这个接口中的 test(T t) 方法,入参的个数是 1,返回值是 boolean 类型,它和 java.util.function.Function 接口有些相似,两者的差异如下表所示 ⬇️

接口 抽象方法 返回值类型
java.util.function.Function<T, R> R apply(T t) R (是引用类型)
java.util.function.Predicate<T> boolean test(T t) boolean (是原始类型)

由于 FunctionConsumerSupplier 这三个接口都涉及泛型,所以如果用它们来处理 int/long/double 之类的原始类型,会触发装箱/拆箱操作,这样效率较低,于是 JDK 中又提供了不需要拆箱/装箱操作的,适用于 int/long/double 等原始类型的 functional interface

int 为例,列举相关 functional interface

int 为例,FunctionConsumerSupplier 的对应版本分别如下(为了方便做对比,普通的 FunctionConsumerSupplier 也列在下表里了) ⬇️

接口 代码 抽象方法 类型转化 可以避免 intInteger 之间的装箱/拆箱操作吗?
java.util.function.Function<T, R> Function.java R apply(T t) <math xmlns="http://www.w3.org/1998/Math/MathML"> ( T ) → R (T){\rightarrow}R </math>(T)→R
java.util.function.IntFunction IntFunction.java R apply(int value) <math xmlns="http://www.w3.org/1998/Math/MathML"> ( i n t ) → R (int){\rightarrow}R </math>(int)→R
java.util.function.ToIntFunction<T> ToIntFunction.java int applyAsInt(T value) <math xmlns="http://www.w3.org/1998/Math/MathML"> ( T ) → i n t (T){\rightarrow}int </math>(T)→int
java.util.function.IntUnaryOperator IntUnaryOperator.java int applyAsInt(int operand) <math xmlns="http://www.w3.org/1998/Math/MathML"> ( i n t ) → i n t (int){\rightarrow}int </math>(int)→int
java.util.function.Consumer<T> Consumer.java void accept(T t) <math xmlns="http://www.w3.org/1998/Math/MathML"> ( T ) → v o i d (T){\rightarrow}void </math>(T)→void
java.util.function.IntConsumer IntConsumer.java void accept(int value) <math xmlns="http://www.w3.org/1998/Math/MathML"> ( i n t ) → v o i d (int){\rightarrow}void </math>(int)→void
java.util.function.Supplier<T> Supplier.java T get() <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ) → T (){\rightarrow}T </math>()→T
java.util.function.IntSupplier IntSupplier.java int getAsInt() <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ) → i n t (){\rightarrow}int </math>()→int

用于支持不同数量的参数的 functional interface

有时候,我们需要处理不止一个入参,那么上面提到的 functional interface 就都不太方便,而 JDK 里提供了可以处理 2 个入参的 functional interface (如果参数个数多于 2, 可以通过反复应用这些处理 2 个入参的 functional interface 来完成目标)。 当入参个数为 2 时,FunctionConsumer 对应的接口列举如下(为了方便做对比,普通的 FunctionConsumer 也列在下表里了)⬇️

接口 代码 抽象方法 入参个数
java.util.function.Function<T, R> Function.java R apply(T t) 1
java.util.function.BiFunction<T, U, R> BiFunction.java R apply(T t, U u) 2
java.util.function.Consumer<T> Consumer.java void accept(T t) 1
java.util.function.BiConsumer<T, U> BiConsumer.java void accept(T t, U u) 2

注意,由于 java 中不支持返回多个值,所以没有 BiSupplier 这种接口。

相关推荐
自强的小白25 分钟前
学习Java24天
java·学习
Ashlee_code1 小时前
香港券商櫃台系統跨境金融研究
java·python·科技·金融·架构·系统架构·区块链
还梦呦1 小时前
2025年09月计算机二级Java选择题每日一练——第五期
java·开发语言·计算机二级
2501_924890522 小时前
商超场景徘徊识别误报率↓79%!陌讯多模态时序融合算法落地优化
java·大数据·人工智能·深度学习·算法·目标检测·计算机视觉
從南走到北2 小时前
JAVA国际版东郊到家同城按摩服务美容美发私教到店服务系统源码支持Android+IOS+H5
android·java·开发语言·ios·微信·微信小程序·小程序
qianmoq3 小时前
第04章:数字流专题:IntStream让数学计算更简单
java
带只拖鞋去流浪4 小时前
Java集合(Collection、Map、转换)
java
超级小忍4 小时前
使用 GraalVM Native Image 将 Spring Boot 应用编译为跨平台原生镜像:完整指南
java·spring boot·后端
野犬寒鸦4 小时前
力扣hot100:搜索二维矩阵与在排序数组中查找元素的第一个和最后一个位置(74,34)
java·数据结构·算法·leetcode·list
cxyxiaokui0015 小时前
线程池的“变形记”:核心线程数居然能随时变大变小?
java·面试