Higher Order Functions in Kotlin

Niyati Thakkar Jan 03, 2023
  1. Higher-Order Functions in Kotlin
  2. Pass Function as a Parameter in Kotlin
  3. Kotlin Function as a Return Value
Higher Order Functions in Kotlin

This tutorial teaches how to pass a function as a parameter and return a function using a function in Kotlin.

Functions in Kotlin are very flexible, and they can be stored in data structures, stored in variables, passed as an argument to a function, or returned by another function. They can do everything that the non-function variable does.

Higher-Order Functions in Kotlin

Higher-order functions in Kotlin are the functions that return a function or take a function as an argument or both. In other words, in Kotlin, you can have a function that takes another function as a parameter, return a function as a result, or does both, and these functions are called higher-order functions.

Higher-Order Functions in Kotlin

Let us see an example to see how it works.

fun main(){

    fun hello() {
        println("In hello!")
    }

    fun random(func : () -> Unit){
        println("In random!")
        println("calling hello")
        func()
    }

    random(::hello)
}

Output:

In random!
calling hello
In hello!

Explanation:

  1. Firstly, we have two other functions in the main function, hello and random.

  2. hello() takes no parameters and does not return any value (i.e., returns Unit).

  3. random() takes one parameter, i.e., a function called func locally in random’s scope and doesn’t return any value. The func() function doesn’t take any arguments and returns a Unit, implying it doesn’t return a value.

    Therefore, func is a local name given to the function passed as a parameter while calling random, : (colon) is as usual written after the name of the variable to differentiate behavior and variable name, inside () brackets parameters required by the func function are written and after -> return type of the function is required.

  4. Lastly, we pass the hello function to random in the main method.

Let us see the output to understand the flow of the program.

First, as we call random from the main function passing hello as an argument, we are inside the random function. Thus we get the above two lines printed from the random.

Now random called func, which is nothing but the hello function as we passed it as an argument and thus in the last line, it is in the hello function.

Lambda Expression in Kotlin

Lambda Expressions are function literals or anonymous functions in which we can write a function in a short and readable way. They also make the code look compact and clean.

For example:

val sum = {num1: Int , num2: Int -> num1 + num2}

The sum takes two Int variables, num1 and num2, and returns an Int num1+num2 (i.e., the sum of both numbers).

Pass Function as a Parameter in Kotlin

We can pass a function as a parameter to another function. Both these functions are free to have any return type and can have any number of data type parameters.

Also, the parameter functions can have no parameters and/or no return value.

Example 1:

Let us see an example of an Employee class having two functions, years that calculates the difference of years between two dates and print_details that prints the employee details.

import java.time.LocalDate

// Employee class
public class Employee(id: Int, name: String, joining_date: LocalDate, leaving_date: LocalDate) {
    var name: String = name
    var id: Int = id
    var joining_date: LocalDate = joining_date
    var leaving_date: LocalDate = leaving_date
}

// function years returns difference between numbers of years of joining_date and leaving_date
fun years(date1 : LocalDate, date2 :LocalDate) : Int{
    return date1.getYear()-date2.getYear()
}

// printing employee detains and years served to the company by the employee
fun print_details(emp: Employee, years: (LocalDate, LocalDate) -> Int) {
    println(
        "Employee ID: ${emp.id}\n" +
                "Employee Name: ${emp.name}\n" +
                "Years served: ${years(emp.leaving_date, emp.joining_date)}"
    )
}

// main function
fun main(args: Array<String>) {

// object of Employee class
    var Raghav = Employee(1004, "Raghav Mishra", LocalDate.parse("2005-09-30"), LocalDate.parse("2012-11-17"))

    // printing detals
    print_details(Raghav, ::years)
}

Output:

Employee ID: 1004
Employee Name: Raghav Mishra
Years served: 7
  1. Employee class objects have four parameters: name, id, joining_date, and leaving_date. The joining_date and leaving_date are of the data type LocalDate.
  2. The year function takes two LocalDate variables. The getYear() returns year in the particular LocalDate and the year function returns the difference of the years in two dates as Int.
  3. The print_details takes an object of the Employee class and a function to calculate years of service by the employee. It prints Employee ID, Employee Name, and years of service by passing joining_date and leaving_date to years function.
  4. In the main method, we create an object of the Employee class, and we call the print_details function by passing the employee Raghav and :: years function that prints the details in the output.
Note
The :: operator (in this context) creates a callable reference to the function.

Example 2:

To understand and apply this feature of Kotlin, let us take a more practical example for filtering a list.

fun main(args: Array<String>) {
    var nums = listOf(153, 534,773,894,247,52)
    fun odd(n : Int) : Boolean = n%2!=0
    println("Odd numbers in the list are:")
    nums.filter(::odd).forEach {n -> println(n)}
}

Output:

Odd numbers in the list are:
153
773
247

We take a list of numbers called nums. Function odd takes an Int number as an argument and returns true if the number is odd.

The filter function takes a function as an argument which takes an element of nums as a parameter and returns a Boolean. Here nums is a list of Int values; thus, odd must take Int as an argument and return a Boolean.

All the elements that result in true in an odd function are passed to (i.e., filtered) forEach function. The forEach() function takes a Lambda expression or a function as a parameter and returns a Unit, printing each number to a new line.

Note:

nums.filter(::odd).forEach {n -> println(n)}

The above code can also be written like below to get the same results.

nums.filter(::odd).forEach(::println)
//or
nums.filter{ odd(it) }.forEach { println(it) }
//or
nums.filter{odd(it)}.forEach(::println)

The it can be replaced by some other name like in a Lambda function: nums.filter{n -> odd(n)}

Thus to sum up: forEach{n -> println(n)} == forEach{println(it)} == forEach(::println)

Kotlin Function as a Return Value

Higher-order functions are also the functions that return another function. Again returned function can have a return value or return a Unit and take no or any number of parameters.

Let us see an example of a calculator, where we take user input of 2 numbers and give the user a choice of how to operate these numbers, and finally return the result.

We will take two functions, and the first one returns a function as per the choice given by the user. The second function takes the function returned by the first function and operates.

// add func
fun add(a: Int, b: Int) : Int = a+b
// sub func
fun subtract(a: Int, b: Int) : Int = a-b
// multiply func
fun multiply(a: Int, b: Int) : Int {return a*b}
// division func
fun divide(a: Int, b: Int) : Int {return a/b}
// incorrect input
fun error(a: Int, b: Int) : Int {
    println("error");
    return -1
}

// choose returns a function requested by the user
fun choose(choice : Int) : (a : Int, b : Int)-> Int{
    return when(choice){
        1 -> ::add
        2 -> ::subtract
        3 -> ::multiply
        4 -> ::divide
        else -> ::error
    }
}

// function returning a function
fun perform(a : Int, b : Int, action : (a : Int, b : Int)-> Int) : Int{
    return action(a,b)
}

fun main(){
    println("Enter first number")
    var a = Integer.parseInt(readLine())
    println("Enter second number")
    var b = Integer.parseInt(readLine())
    println("Enter choice:\n1 add\n2 subtract\n3 multiply\n4 divide")
    var choice = Integer.parseInt(readLine())
    var result = perform(a,b,choose(choice))
    println("Result = $result")
}

Output:

Enter the first number
8493
Enter the second number
3
Enter choice:
1 add
2 subtract
3 multiply
4 divide
4
Result = 2831

In the main function, we ask the user for 2 numbers first and then ask for choice. In the choice method, we pass the user’s choice.

The choice function returns a function as selected by the user. Therefore, we have created five functions: add, sub, multiply, divide, and error.

One of these is returned by the choice function. This function is then passed to the perform function, it takes two numbers of Int type, and a function returned by the choice function say func.

Two user-entered numbers are passed to func, and the result returned by func is printed as output.

Also, note that the add and sub are initialized with the = (equal to) sign. The multiply and divide are initialized with {}.

Both are correct and can be used but add and sub is simpler to understand.

Pass Lambda Expression Instead of Function in Kotlin

In Kotlin, we can pass a Lambda expression instead of a function if we have to use the function only once and the function is a one-line expression. Say, for instance, instead of passing the divide function; we choose to pass the expression directly to perform the function as below.

var res = perform(a,b, { a,b -> a/b })
Niyati Thakkar avatar Niyati Thakkar avatar

Niyati is a Technical Content Writer and an engineering student. She has written more than 50 published articles on Data Structures, Algorithms, Git, DBMS, and Programming Languages like Python, C/C++, Java, CSS, HTML, KOTLIN, JavaScript, etc. that are very easy-to-understand and visualize.

LinkedIn

Related Article - Kotlin Function