Kotlin Higher-Order Functions

A higher order function is a function that takes a function as a parameter or it returns a function. It allows us to write shorter and more beautiful code. Let‘s look at some examples.

fun <T> tryAndCatch(block: () -> T) = try {
    block()
} catch (e: Exception) {
    println(e.message)
}

This function will run code passed inside block variable inside try catch block and return result and also print exception in case it is thrown. What does it all mean?

<T> is function type. It is passed by this command block: () -> T. This means that function accepts as parameter function without any parameters returning object of type T. So basically anything. Of course we can also accept functions with specific parameters as well as pass both variables and functions into function.

fun doOperationWithTwoNumbersAndReturnResultAsString(num1: Int, num2: Int, fn: (Int, Int) -> Int ) : String {
    val result = fn(num1, num2)
    return result.toString()
}

This function accepts 2 Integers and a function accepting two Integers and returning Integer, calls this function and return result as String.

How can we call this function? There are multiple options

Lambda

Lambda expression is same as an anonymous function a “function literal”, a function that is not declared but passed to function as an expression. Lambda expression is always surrounded by curly braces.

The most simple example of lambda is one without any parameters. Let’s imagine we have a function that accepts Integer number and any function without parameter returning number. Like this one.

fun addIntegerToResult(fn: () -> Int, num: Int) : Int = fn() + num

Using lambda we can call this function this way.

addIntegerToResult({10*2}, 3)

Now we will override this function to accept one parameter.

fun addIntegerToResult(fn: (num: Int) -> Int, num: Int) : Int = fn(num) + num

If we added function to same file, Android Studio will now show error for our code calling this function.

The reason is that for lambda with only one parameter we can skip it and call it exactly same as function without any parameters. So if we need to use lambda both without parameters and with one, we need to rename function. But how do we use parameter then?

We can use keyword it.

addIntegerToResult2({it * it}, 3)

If we decide to include parameter, we can pass this way.

addIntegerToResult2({num: Int -> num * num}, 3)

We can also skip parameter type.

addIntegerToResult2({num -> num * num}, 3)

Anonymous function

Second option what to pass as function is anonymous function.

addIntegerToResult2(fun(num: Int) : Int = num + num, 5)

Normal function

We can also pass normal function. For example this one:

fun multiply(num: Int) : Int = num * num

How do we pass this function as a parameter? We use :: to signify function reference.

addIntegerToResult2(::multiply, 8)

If we need to pass class member function, simply pass object instance before ::

class SomeObject(val name: String) {
    fun nameToUpperCase() : String = name.toUpperCase()
}

val someObject = SomeObject("Jim")
println(addStarsToText(someObject::nameToUpperCase))