java
public static <T> String convert(String idString,
Function<List<String>, List<T>> queryFunction,
Function<T, String> idGetter,
Function<T, String> nameGetter){
//具体实现
}
工具类原文链接:我用夸克网盘给你分享了「逗号分隔ID转名称」,链接:https://pan.quark.cn/s/5996dcb37237
这个convert
方法是一个典型的通用工具方法,核心是通过「泛型」和「函数式接口」实现对不同场景的适配。我们可以从「泛型设计」「参数作用」「逻辑流程」三个角度拆解,理解它为什么能通用,以及如何写出类似的工具类。
一、先看懂:泛型 <T>
是啥?
方法定义中的 <T>
是「泛型参数」,你可以把它理解为一个"占位符",代表任意实体类类型 (比如Subject
、User
、Dept
等)。
-
为什么需要它?
工具类的目的是"通用",如果不写泛型,方法就只能固定处理某一种实体(比如只能处理
Subject
),无法复用。有了<T>
,这个方法可以同时处理User
、Dept
等任何实体,只要它们有"ID"和"名称"字段。 -
举例:
当处理学科时,
T
就是Subject
;处理用户时,T
就是User
。方法会根据实际传入的类型自动适配,不需要重复写多个类似的方法。
二、再拆参数:3个Function
到底在干嘛?
方法的4个参数中,后3个Function
是核心,它们负责把"可变的逻辑"从工具类中抽离出来(让工具类只保留通用逻辑)。
我们用一个具体场景(把课程的"学科ID字符串"转"学科名称")来对应参数,就很容易理解:
参数 | 类型 | 作用 | 例子(学科场景) |
---|---|---|---|
idString |
String |
原始输入:逗号分隔的ID字符串(比如"1,2,3") | 课程的subjectInfo 字段(存学科ID) |
queryFunction |
Function<List<String>, List<T>> |
传入"根据ID列表查实体"的逻辑 | ids -> subjectMapper.selectByIds(ids) (查学科列表) |
idGetter |
Function<T, String> |
传入"从实体中取ID"的逻辑 | Subject::getId (从学科实体中取ID) |
nameGetter |
Function<T, String> |
传入"从实体中取名称"的逻辑 | Subject::getName (从学科实体中取名称) |
java
public String getSubjectNames(TbCourse tbCourse) {
String subjectInfo = tbCourse.getSubjectInfo(); // 逗号分隔的学科 ID 字符串
return IdToNameConverter.convert(
subjectInfo,
// 1. 查询函数:根据 ID 列表查学科
ids -> tbCourseMapper.selectByIds(ids),
// 2. 从学科实体取 ID
TbCourse::getId,
// 3. 从学科实体取名称
TbCourse::getName
);
}
关键:Function
是"输入→输出"的转换器
Function<A, B>
是Java的函数式接口,代表"接收A类型参数,返回B类型结果"的转换规则。
比如 idGetter
是 Function<T, String>
,意思是"接收一个T
类型的实体(比如Subject
),返回一个String
类型的ID"。
三、逻辑流程:通用工具类的"不变"与"变"
这个方法的核心逻辑是固定的流程,但流程中的"具体操作"通过参数传入(即"变"的部分)。整体步骤如下:
原始ID字符串 → 拆分ID列表 → 查实体列表(用queryFunction) → 建ID→名称映射(用idGetter和nameGetter) → 拼接名称字符串
分步拆解(结合学科场景):
-
处理空输入 :如果
idString
是null
或空,直接返回空字符串(避免空指针)。(通用逻辑:所有场景都需要处理空值)
-
拆分ID列表 :把"1,2,3"拆成
["1", "2", "3"]
(去空格、过滤空值)。(通用逻辑:所有"逗号分隔ID"场景都需要拆分)
-
查询实体列表 :调用
queryFunction
,传入拆分后的ID列表,得到实体列表(比如List<Subject>
)。(可变逻辑:查学科用
subjectMapper
,查用户用userMapper
,由参数传入) -
构建ID→名称映射 :遍历实体列表,用
idGetter
取ID、nameGetter
取名称,存到Map
中(比如{"1":"数学", "2":"语文"}
)。(可变逻辑:不同实体的ID和名称字段不同,由参数传入)
-
拼接名称字符串 :按原始ID顺序,从
Map
中取名称拼接(比如"数学, 语文")。(通用逻辑:所有场景都需要按原顺序拼接)
四、为什么这样写能成为"通用工具类"?
通用工具类的核心设计思路是:把"不变的通用逻辑"写死在工具类里,把"变化的具体逻辑"通过参数传进来。
- 不变的逻辑:拆分ID、处理空值、拼接名称(这些是所有"ID转名称"场景共有的)。
- 变化的逻辑:查哪个表、ID字段叫什么、名称字段叫什么(这些因场景而异,通过参数传入)。
这种设计让一个方法能顶多个用,比如同样的convert
方法,既能处理"学科ID转名称",也能处理"用户ID转用户名""部门ID转部门名",不需要重复写代码。
五、自己写通用工具类的3个关键步骤
-
识别"通用逻辑"和"可变逻辑" :
比如"ID转名称"中,拆分、拼接是通用的;查询、取字段是可变的。
-
用泛型
<T>
适配不同类型 :如果你需要处理多种实体(User、Dept等),用泛型代替具体类型,让工具类"不绑定"某一种实体。
-
用函数式接口传递可变逻辑 :
用
Function
(转换)、Predicate
(判断)等接口,把可变的操作通过参数传入(就像把"具体做什么"告诉工具类)。
总结
这个convert
方法之所以通用,是因为它没有硬编码任何具体业务(比如只查学科表),而是通过泛型适配不同实体,通过Function
参数接收具体操作。理解了"泛型占位"和"函数传参"这两个点,就能慢慢写出灵活的通用工具类了。