Higher-order function(高阶函数)——是一个接受另一个函数作为参数或返回函数作为结果的函数。在Kotlin中,函数是一等公民:可以存储在变量中、作为参数传递和返回。根据Kotlin文档(2026),与命令式方法相比,高阶函数平均减少30%的代码重复。Higher-order function——是现代开发中函数式风格的基本概念。
要点
Higher-order function——是一个具有至少两个特征之一的函数:接受另一个函数作为参数或返回函数作为结果。在数学中,这样的函数被称为泛函或算子。在编程中,它们于1958年在Lisp中出现,并成为所有现代语言——JavaScript、Python、Swift、Kotlin、Scala和Haskell的必修部分。高阶函数允许从特定操作中抽象出来,并将行为作为值传递。
高阶函数和普通函数之间的关键区别——存在带有函数类型的参数或返回的函数值。在Kotlin中,函数类型写为(ParamType) -> ReturnType。例如,类型(Int) -> String表示接受Int并返回String的函数。类型() -> Unit表示没有参数、不返回有用值的函数。正是这个类型系统使高阶函数具有类型安全性。
高阶函数的对立面——first-class function(一等函数)。一等函数意味着函数可以像任何其他值一样使用:赋值给变量、存储在集合中、作为参数传递。高阶函数是使用一等函数来接受或返回的函数。Kotlin在语言级别支持这两个概念,无需额外库。
在Kotlin中,任何至少有一个参数声明为函数类型或其返回类型是函数的函数,都自动被视为高阶函数。编译器不需要特殊注释——只需在签名中指定(T) -> R即可。调用此类函数时,作为参数传递的是lambda表达式、通过::对现有函数的引用或存储在变量中的函数值。
fun operate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
return op(a, b)
}
fun main() {
val sum = operate(10, 20) { x, y -> x + y }
println(sum) // 30
}
在列表中,operate函数接受两个整数和类型为(Int, Int) -> Int的参数op。函数体在一行中调用传递的操作。在main中,operate调用传递lambda { x, y -> x + y }——由于trailing lambda语法,Kotlin将其放在括号之后。如果lambda是最后一个参数,它可以完全放在括号之外,这提高了调用链的可读性。
在底层,Kotlin通过Function接口(对于两个参数是Function2)编译高阶函数。每个lambda都转换为实现相应FunctionN接口的匿名类。这意味着创建lambda会为每个操作分配对象。为了减少开销,Kotlin支持内联函数,它将高阶函数体插入调用点,消除匿名类的创建。
将函数作为参数传递——使用高阶函数最常见的模式。程序员不是创建具有多态方法的类层次结构,而是将所需行为直接传递到使用点。这实现了开闭原则(Open/Closed Principle)而无需继承:新操作作为新lambda添加,而不是作为新子类。
对于可空函数,Kotlin在括号后使用问号的类型((T) -> R)?。这样的函数只能在null检查之后或通过?.invoke()运算符调用。高阶函数的签名清楚地表明,传递函数不是必需的——调用代码可以省略参数。这对于具有可选行为的回调和事件处理程序很有用。
fun <T> List<T>.customFilter(
predicate: (T) -> Boolean
): List<T> {
val result = mutableListOf<T>()
for (item in this) {
if (predicate(item)) result.add(item)
}
return result
}
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val even = numbers.customFilter { it % 2 == 0 }
println(even) // [2, 4]
}
扩展函数customFilter接受函数类型(T) -> Boolean的参数predicate。在循环内部,它为每个元素调用predicate并收集匹配的元素。main中的调用传递lambda { it % 2 == 0 },其中it是lambda唯一参数的隐式名称。由于高阶函数,过滤逻辑与集合遍历机制完全分离。
高阶函数可以返回函数——这种模式称为函数工厂或行为生成器。返回的函数可以捕获外部可见性范围内的变量(闭包),并在调用之间保持它们。这允许基于通用模板创建可配置的处理程序和专用操作。
在返回函数时,Kotlin从签名中推断出返回的函数类型((T) -> R)?。编译器检查主体中的所有return表达式是否返回兼容的函数值。捕获的变量存储在lambda对象中,只要存在对它的引用就可以访问。这是一个强大的机制,但需要注意内存管理。
fun makeMultiplier(factor: Int): (Int) -> Int {
return { x -> x * factor }
}
fun main() {
val double = makeMultiplier(2)
val triple = makeMultiplier(3)
println(double(5)) // 10
println(triple(5)) // 15
}
makeMultiplier函数接受factor并返回lambda { x -> x * factor },其中factor从外部范围(闭包)捕获。每次调用makeMultiplier都会创建一个具有自己factor值的新函数。double和triple变量存储返回的函数,可以多次调用。这种模式广泛用于HTTP客户端配置、装饰器和中间件。
结合使用高阶函数和lambda可以在没有中间变量的情况下构建表达式的操作链。Kotlin标准库包含数十个高阶函数:let、run、apply、also、filter、map、flatMap、fold、reduce、forEach、groupBy等。每个都接受一个lambda并借助它执行数据转换。
data class User(val name: String, val age: Int)
fun main() {
val users = listOf(
User("Alice", 25),
User("Bob", 17),
User("Charlie", 30)
)
val result = users
.filter { it.age >= 18 }
.map { it.name.uppercase() }
.sorted()
println(result) // [爱丽丝, 查利]
}
在示例中,三个高阶函数的链处理用户列表。filter接受谓词,只保留成年用户。map将每个用户转换为大写字母的名称。sorted按升序对结果排序。每个操作都接受一个lambda,Kotlin在所有阶段确保类型安全。如果没有高阶函数,则必须编写带有if、临时列表和手动排序的循环。
常见问题
高阶函数接受另一个函数作为参数或返回它。普通函数只处理数据——数字、字符串、对象。高阶函数处理行为,将逻辑作为参数传递。
是的,inline修饰符消除了为lambda创建匿名类的开销。使用crossinline或noinline,可以控制哪些lambda被插入,哪些保持为对象。
没有参数和没有返回值的函数的类型是() -> Unit。具有一个类型T参数和返回R的函数写为(T) -> R。对于两个参数——(T, U) -> R,依此类推直到22个参数。
Lambda——紧凑记法{ args -> body },匿名函数——fun(args): ReturnType { body }。lambda不能有无标签的return,匿名函数可以。两者都可以传递给高阶函数。
在热循环中避免没有inline的高阶函数——每个lambda创建一个对象。对于关键性能,使用inline fun。另外,不要滥用lambda的深度嵌套——这会降低可读性。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。