In Kotlin, almost everything is a value that can be stored, including functions, thus they are first class. When you can store something in a variable, you can pass it around as function parameters or have them as return types.
Higher-order functions are functions that take in other functions as parameters or use them as return types.
fun Int.solve(expression: (Int, Int) -> Int, a: Int, b: Int): Int { return expression(a, b) }
Some more examples include:
fun main(args: Array<String>) { val sum = func(10) println("10 + 20: ${sum(20)}") } fun func(num: Int): (Int) -> Int = {num2 -> num2 + num}
fun attemptUnlock(solution: () -> Unit) { solution() // Throws an error if unlock fails unlock() }
fun mapDataBeforeApplying(mapData: (DataModel) -> UiModel, data: DataModel) { val uiModel = mapData(data) updateUi(uiModel) }
Use Cases of Higher-Order Functions
As you can see from the examples above, we can use higher-order functions to either acquire different values from the same set of parameters like with the Int.solve function.
We can also use them as a way to precede other actions in a given function as a more concise way of using if’s and switch statements, like we did in the mapDatabeforeApplying function.
Higher-Order Functions and Inline Functions
Higher-Order functions are not without cost. Since each function is an object, and they capture closure (variables accessed in the body of the function), these memory allocations and virtual calls introduce runtime overhead.
This kind of overhead can be eliminated with the use of inline functions.
inline fun <T> lock(lock: Lock, body: () -> T): T { ... }
Use of inlining may cause generated code to grow, as detailed in the Kotlin Docs. The ideal solution is to balance use of generated code with inlining with the use of higher-order functions, all with the goal of nice, maintainable code with minimal memory overhead.