Open In App

Local Functions in Kotlin

Last Updated : 22 Feb, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

The idea behind functions is very simple: split up a large program into smaller chunks that can be reasoned more easily and allow the reuse of the code to avoid repetition. This second point is known as the DRY principle: Don’t Repeat Yourself. The more the number of times you write the same code, the more the chances you create of a bug creeping in. When this principle is taken to its logical conclusion, you would have created a program that consists of many small functions, each doing a single thing; this is similar to the Unix principle of small programs, where each program does a single job. The same principle applies to the code inside a function. Typically, in say Java, a large function or method might be broken down by calling several support functions declared in either the same class or a helper class that contains static methods.

Example

Kotlin allows us to take this a step further by supporting functions declared inside other functions. These are called local or nested functions. Functions can even be nested multiple times. The example of printing areas can be written in the following style:

Kotlin




fun printArea(width: Int, height: Int): Unit {
  fun calculateArea(width: Int, height: Int): Int = width * height
  val area = calculateArea(width, height)
  println("The area is $area")
}


As you can see, the calculateArea function is now inside printArea and thus not accessible to the code outside. This is useful when we want to hide functions that are just used as implementation details of a larger function. We could also achieve a similar effect by defining a member function as private. So do local functions have any other advantages? Yes, they do! Local functions can access the parameters and variables defined in the outer scope:

Kotlin




fun printArea2(width: Int, height: Int): Unit {
  fun calculateArea(): Int = width * height
  val area = calculateArea()
  println("The area is $area")
}


Notice that we’ve removed the parameters from the calculateArea function, and now it directly uses the parameters defined in the enclosing scope. This makes the nested function more readable and saves repeating the parameter definitions, which is very useful for functions with many parameters. Let’s work through an example of a function that could be broken down using local functions:

Kotlin




fun fizzbuzz(start: Int, end: Int): Unit {
  for (k in start..end) {
    if (k % 3 == 0 && k % 5 == 0)
        println("Fizz Buzz")
    else if (k % 3 == 0)
        println("Fizz")
    else if (k % 5 == 0)
        println("Buzz")
    else
        println(k)
  }
}


This is the well-known Fizz Buzz problem. The requirement asks you to print out the integers from the start to the end value. However, if the integer is a multiple of 3, you should print Fizz. If it is a multiple of 5, you should print Buzz. If it is a multiple of 3 and 5, then print Fizz Buzz together.

The first solution is short and readable, but it duplicates some code. The modulo checks are coded twice, which doubles the potential for a bug. Clearly, this example is extremely simple, so the chances of a typo are minimal; however, it serves to demonstrate the issue for larger problems. We can declare a local function for each of the modulo checks so that we only have to code it once. This brings us to the next iteration of our solution:

Kotlin




fun fizzbuzz2(start: Int, end: Int): Unit {
  fun isFizz(k: Int): Boolean = k % 3 == 0
  fun isBuzz(k: Int): Boolean = k % 5 == 0
  for (k in start..end) {
    if (isFizz(k) && isBuzz(k))
        println("Fizz Buzz")
    else if (isFizz(k))
        println("Fizz")
    else if (isBuzz(k))
        println("Buzz")
    else
        println(k)
  }
}


Here, our if…else branches now invoke the nested functions isFizz and isBuzz. However, it is still a bit verbose to pass k to the function each time. Is there a way we can avoid this? Turns out, the answer is yes! We can define local functions not just directly inside other functions, but also in for loops, while loops, and other blocks:

Kotlin




fun fizzbuzz3(start: Int, end: Int): Unit {
  for (k in start..end) {
    fun isFizz(): Boolean = k % 3 == 0
    fun isBuzz(): Boolean = k % 5 == 0
      
    if (isFizz() && isBuzz())
        println("Fizz Buzz")
    else if (isFizz())
        println("Fizz")
    else if (isBuzz())
        println("Buzz")
    else
        println(k)
  }
}


In this third iteration of our function, we have moved the function definitions inside the for loop. So now, we can omit the parameter declarations and access k directly. Finally, we could take advantage of the Kotlin Basics, to remove some of the noise of the if…else keywords:

Kotlin




fun fizzbuzz4(start: Int, end: Int): Unit {
    for (k in start..end) {
      fun isFizz(): Boolean = k % 3 == 0
      fun isBuzz(): Boolean = k % 5 == 0
    when {
      isFizz() && isBuzz() -> println("Fizz Buzz")
      isFizz() -> println("Fizz")
      isBuzz() -> println("Buzz")
      else -> println(k)
      }
  }
}


This gives us our final solution, which avoids the repetition of code and is more readable than the initial iteration.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads