1. 概述
Kotlin 旨在成为一门支持函数式编程范式的语言,这意味着它允许开发者以函数为基本单元来组织程序逻辑。函数式编程强调将程序视为一系列函数调用的组合,这些函数接受输入并返回输出,避免副作用。
在本教程中,我们将探讨 Kotlin 中的高阶函数 以及 如何将接口作为方法参数使用,并结合实际代码示例说明其应用场景和优势。
2. 声明和调用高阶函数
高阶函数指的是接受函数作为参数或返回函数作为结果的函数。在 Kotlin 中声明高阶函数非常简单。
✅ 最直观的方式是使用 lambda 表达式:
fun performAction(action: () -> Unit) {
// Action 之前的操作
action()
// Action 之后的操作
}
// 调用方式:
performAction { println("Hello, world!") }
上述示例虽然简单,但功能有限,因为它只能接受无参数、无返回值的函数,通常用于执行副作用。
✅ 使用 typealias 简化函数类型定义
当多个高阶函数需要使用相同类型的 lambda 参数时,可以使用 typealias
来简化类型定义:
typealias MySpecialSupplier = () -> SuppliedType
fun supplyWithTiming(supplier: MySpecialSupplier): SuppliedType =
measureTimedValue { supplier() }
.let {
println("调用耗时 ${it.duration}ms")
it.value
}
这样可以提高代码的可读性和复用性。
✅ 使用函数式接口(Functional Interface)
如果一个接口只包含一个抽象方法,就可以用 fun interface
声明,Kotlin 会自动支持 SAM 转换(Single Abstract Method),从而允许我们使用 lambda 表达式传参:
fun interface Mapper {
fun toSupply(name: String, weight: Int): SuppliedType
}
fun verifiedSupplier(name: String, weight: Int, mapper: Mapper): SuppliedType {
println("执行校验逻辑")
return mapper.toSupply(name, weight)
}
// 调用方式:
verifiedSupplier("Sugar", 1) { name, weight -> SuppliedType(name, weight) }
✅ 使用泛型高阶函数
对于更通用的高阶函数,我们可以使用泛型来提升灵活性,例如 Kotlin 标准库中的 mapTo
函数:
public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>
.mapTo(destination: C, transform: (T) -> R): C {
for (item in this)
destination.add(transform(item))
return destination
}
✅ 支持柯里化(Currying)和部分应用(Partial Application)
柯里化是函数式编程中常见的技巧,用于将一个接受多个参数的函数转换为一系列接受单个参数的函数:
fun <T> curry(a: T, bifunction: (T, T) -> T): (T) -> T = { b -> bifunction(a, b) }
val plusFour = curry(4) { a, b -> a + b }
assert(plusFour(2) == 6)
这种技巧可以帮助我们更好地分离关注点。
3. 高阶函数的实际应用场景
高阶函数非常适合用于封装那些与业务逻辑无关的通用逻辑,比如日志记录、性能监控、异常处理、超时控制等。
❓ 举个例子:下载图片并处理超时与异常
原始代码可能如下:
val imageResponse = try {
clientExecutor.submit(Callable { client.get(imageUrl) })
.get(10L, TimeUnit.SECONDS)
} catch (ex: Exception) {
logger.error("下载图片失败: $imageUrl")
return
}
这段代码做了很多事:提交任务、设置超时、捕获异常、记录日志。它混杂了业务逻辑和技术细节,不够清晰。
✅ 拆解逻辑:提取通用逻辑为高阶函数
我们可以将其拆分为两个通用函数:
1. 超时控制封装
fun <T> timeout(timeoutSec: Long, supplier: Callable<T>): T =
clientExecutor.submit(supplier).get(timeoutSec, TimeUnit.SECONDS)
2. 异常捕获与日志封装
fun <T> successOrLogException(exceptionMsg: String, supplier: () -> T): T? = try {
supplier()
} catch (ex: Exception) {
logger.error(exceptionMsg)
null
}
3. 合并使用
val imageResponse = successOrLogException("下载图片失败: $imageUrl") {
timeout(10L) { client.get(imageUrl) }
} ?: return
这样写的好处是:逻辑清晰、职责分离、复用性强。我们只需声明“想要做什么”,而不是“怎么做”。
4. 总结
在本篇文章中,我们学习了 Kotlin 中的高阶函数及其多种用法:
- ✅ 使用 lambda 表达式作为函数参数
- ✅ 使用 typealias 简化函数类型
- ✅ 使用函数式接口配合 SAM 转换
- ✅ 使用泛型提升函数通用性
- ✅ 支持柯里化与部分应用
- ✅ 将非业务逻辑抽离为高阶函数,提升代码可读性与可维护性
高阶函数让我们可以更清晰地表达程序意图,而不是堆砌实现细节。这种声明式风格的代码更容易阅读、测试和维护。
所有示例代码均可在 GitHub 仓库 上找到。