在kotlin实践过程中,函数名可以被当作普通变量一样使用。函数可以被当作值来传递、赋值给变量,也可以作为参数传递给其他函数,甚至可以作为函数的返回值。而合理使用高级函数,可以对一些重复代码进行抽象,从而达到复用的目的,以下笔者将结合实践经历,对这一函数进行实践和分析。
我们先看这样一个示例,入参有一个operate函数,在传参时可以自定义函数的实现。
fun add(a: Int, b: Int, operate: (Int) -> Int): Int {
return operate(a + b)
}
fun test() {
add(1, 2) {
value -> value / 2
}
// 输出: 1
}
业务实践中,我们设想这样一个场景,有一系列的数据源,mysql、doris、tidb等等,然后需求是每次都要从另外一个mysql分页查询数据,然后对这一些列的数据源里的数据做一些合并操作处理,而且操作这些数据源的时候,是有依赖关系的,前者处理完后,发送mq处理下一个数据源。代码示例如下:
fun handleTidb(mergeTask: MergeTaskVo) {
var minId = mereDataMapper.fetchMinId(mergeTask) - 1
var mergeDataList = mereDataMapper.listPage(minId, DEFAULT_SIZE, mergeTask)
while (!CollectionUtils.isEmpty(mergeDataList)) {
val studentPerformanceDetailList = mutableListOf<StudentPerformanceDetail>()
for (item in mergeDataList) {
val studentPerformanceDetailVo = MergeDataVo().toStudentPerformanceDetail(item)
studentPerformanceDetailList.add(studentPerformanceDetailVo)
}
performanceMergeService.batchModifyTidbTeacherPerformance(studentPerformanceDetailList)
minId = mergeDataList[mergeDataList.size - 1].id!!
mergeDataList = mereDataMapper.listPage(minId, DEFAULT_SIZE, mergeTask)
}
// 发送mq处理tidb数据
msgProducer.sendMsg(topic, mysql_tag, mergeTask.taskId, mergeTask)
}
fun handleMysql(mergeTask: MergeTaskVo) {
var minId = mereDataMapper.fetchMinId(mergeTask) - 1
var mergeDataList = mereDataMapper.listPage(minId, DEFAULT_SIZE, mergeTask)
while (!CollectionUtils.isEmpty(mergeDataList)) {
val studentPerformanceDetailList = mutableListOf<StudentPerformanceDetail>()
for (item in mergeDataList) {
val studentPerformanceDetailVo = MergeDataVo().toStudentPerformanceDetail(item)
studentPerformanceDetailList.add(studentPerformanceDetailVo)
}
performanceMergeService.batchInsertPerformanceDetail(studentPerformanceDetailList)
minId = mergeDataList[mergeDataList.size - 1].id!!
mergeDataList = mereDataMapper.listPage(minId, DEFAULT_SIZE, mergeTask)
}
msgProducer.sendMsg(topic, doris_tag, mergeTask.taskId, mergeTask)
}
// ...
上面的例子中我们可以看到有着很多重复的代码,纵观上面的操作,其实就真正操作数据库时的那个步骤不太同,那么这里我们可以用高级函数进一步抽象:
这里,我们抽象出了 operate 函数,然后再看 handleTidb 方法,一下子就简洁易懂了很多。
protected fun baseOperate(mergeTask: MergeTaskVo, operate: (List<StudentPerformanceDetail>) -> Unit) {
// 初始化的最小id减去1, 便于后边每次查询能统一包含最小id的记录
var minId = mereDataMapper.fetchMinId(mergeTask) - 1
var mergeDataList = mereDataMapper.listPage(minId, DEFAULT_SIZE, mergeTask)
while (!CollectionUtils.isEmpty(mergeDataList)) {
val studentPerformanceDetailList = mutableListOf<StudentPerformanceDetail>()
for (item in mergeDataList) {
val studentPerformanceDetailVo = MergeDataVo().toStudentPerformanceDetail(item)
studentPerformanceDetailList.add(studentPerformanceDetailVo)
}
operate(studentPerformanceDetailList)
minId = mergeDataList[mergeDataList.size - 1].id!!
mergeDataList = mereDataMapper.listPage(minId, DEFAULT_SIZE, mergeTask)
}
msgProducer.sendMsg(Constants.CLASS_MERGE_MESSAGE_TOPIC, Constants.HANDLE_TIDB_PERFORMANCE_TYPE_RECORD_TAG, mergeTask.taskId, mergeTask)
}
fun handleTidb(mergeTask: MergeTaskVo) {
baseOperate(mergeTask) {
param -> performanceMergeService.batchModifyTidbTeacherPerformance(param)
}
// 发送mq处理tidb数据
msgProducer.sendMsg(topic, mysql_tag, mergeTask.taskId, mergeTask)
}
关于它的原理其实也很简单,其实就是一个语法糖,当我们查看它的字节码,然后反编译成Java字节码时,本质上还是跟我们在每一个函数方法里重写了查库逻辑。具体操作, 选中文件 -> Tools -> Kotlin -> Show Kotlin Bytecode:
反编译后Java文件:
public void handleTidb(@NotNull MergeTaskVo mergeTask) {
Intrinsics.checkParameterIsNotNull(mergeTask, "mergeTask");
int minId = this.getMereDataMapper().fetchMinId(mergeTask) - 1;
for(List mergeDataList = this.getMereDataMapper().listPage(minId, this.getDEFAULT_SIZE(), mergeTask); !CollectionUtils.isEmpty((Collection)mergeDataList); mergeDataList = this.getMereDataMapper().listPage(minId, this.getDEFAULT_SIZE(), mergeTask)) {
List studentPerformanceDetailList = (List)(new ArrayList());
Iterator var6 = mergeDataList.iterator();
while(var6.hasNext()) {
MergeData item = (MergeData)var6.next();
StudentPerformanceDetail studentPerformanceDetailVo = (new MergeDataVo()).toStudentPerformanceDetail(item);
studentPerformanceDetailList.add(studentPerformanceDetailVo);
}
this.getPerformanceMergeService().batchModifyTidbStudentPerformance(studentPerformanceDetailList);
Integer var10000 = ((MergeData)mergeDataList.get(mergeDataList.size() - 1)).getId();
if (var10000 == null) {
Intrinsics.throwNpe();
}
minId = var10000;
}
}