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 仓库 上找到。


原始标题:Interfaces as Method Parameters and Higher Order Functions in Kotlin