Kotlin supports the notion of functions defined within other functions. This type of function is known as a local function. Local functions can be defined quite similarly to other functions, with two exceptions:
- A local function must be defined within another function.
- Local functions do not support visibility modifiers.
To better understand local functions, we'll use an example. In this code, we've defined two local functions, handleSuccess and handleError, within the handleNetworkResponse function:
fun handleNetworkResponse(response: Response<Person>) {
fun handleSuccess(person: Person) {
// handle successful response
}
fun handleError(error: Throwable?) {
// handle error
}
when (response.isSuccess) {
true -> handleSuccess((response.data))
false -> handleError(response.error)
}
}
These two local functions only exist within the scope of the enclosing handleNetworkResponse function. This means they are not callable outside the enclosing function, but they do have access to the arguments and variables of the enclosing function.
To demonstrate how the argument values are accessible within local functions, we've added an errorMsgPrefix variable to the top of handleNetworkResponse, and then consumed that variable within the implementation of our handleError local function:
fun handleNetworkResponse(response: Response<Person>) {
val errorMsgPrefix = "Error occured: "
fun handleSuccess(person: Person) {
// handle successful response
}
fun handleError(error: Throwable?) {
println(errorMsgPrefix + " with code ${response.code}"
+ error?.message)
}
when (response.isSuccess) {
true -> handleSuccess((response.data))
false -> handleError(response.error)
}
}
After adding the additional errorMsgPrefix variable and the response function argument, both are accessible from within the handleError local function, making it possible to share a state between local functions and their enclosing functions.
Local functions can be useful if you want to refactor some code for readability but don't need it to be available to anything else. Rather than writing a separate private function, you could make it a local function instead. This makes it impossible to test the local function in isolation, so the decision to use a local function may depend on whether its implementation needs to be testable on its own, or if it can be considered an implementation detail.
While local functions are somewhat complex when compared to a typical top-level function, Kotlin provides additional mechanisms with which to make the function declarations as concise as possible. In the next section, we'll explore the concept of single expression functions.