Like other programming languages, Kotlin has functions. But unlike Java, Kotlin supports first-class functions. This—and several other nice Kotlin function features—enables several joyful programming patterns.
Functions in Kotlin can be saved in variables and as part of classes, and passed to and return from other functions.
In the example above both
createGreeting are known as
A higher-order function is a function that either takes one or more functions as
arguments or returns a function.
Named functions in Kotlin are declared using the
Like variable declarations, types—including the return type—appear
to the right of the declaration, separated by a colon.
Here’s a Kotlin function that adds two
Fairly similar to Java, except that the types are on the other side of the names:
Kotlin functions don’t have to accept parameters or return a value:
If the return type of a function is omitted it is inferred as
Unit, which is
Kotlin’s equivalent of Java`s void:
Note that Kotlin will not infer types for functions. So this is not valid:
You may wonder—given that the Kotlin compiler is so great at type inference, why won’t it do this?
As far as I can tell there are two reasons. First, including types in the function declaration allows Kotlin to check the function body to make sure that it is correct. That allows it to correct errors like this:
Function declarations are short and usually written with an idea of what the function is trying to do—meaning the parameter and return types are known. Function bodies are much longer and are much more likely to contain errors. So Kotlin treats the function declaration as the source of truth about types, rather than the body. That seems reasonable.
Second, inferred parameter types requires looking at how the function is used. For example, how would Kotlin infer the parameter types for the following function:
The first call could be right and the method missing a
Or the second call could be right and first call incorrect.
Or maybe the types of
second should be
Any, allowing both calls
to be correct but forcing the function body to handle multiple types?
Regardless, note that we are allowing the callers of a function to control the parameter types that the function must accept. This seems like a terrible idea. So typing function parameters allows the function writer to ensure that callers use it correctly. Again, that seems reasonable.
Support for higher-order functions is based on support for so-called lambda expressions, sometimes also known as anonymous functions.
In contrast to the named functions described above, anonymous functions are
not declared using the
fun keyword and not assigned a name.
Anonymous functions may be saved to variables and passed to and returned from
In these case we refer to the anonymous function using the name of the variable
that is referring to it, but that does not make it a named function!
Consider the following example:
timeFunction is a higher-order function that takes a single function as an
But what are we passing to it?
The first block that contains
println("Hello") and the second that contains
the loop are both lambda, or anonymous, functions.
Calling them anonymous makes sense, because they never have a name in the body
main, and are only named as
method during the call to
To declare an anonymous function to assign to a variable, we use curly braces to enclose the block of code representing the function. We can then call the function like any other named function:
Also note in the example above that both
second refer to the
same anonymous function.
So the function remains anonymous, even if we have two variables that both refer
Anonymous functions can accept arguments. They are listed after the opening curly brace, separated by commas, and separated from the function body by a right arrow:
And, just as when we declare functions using the
fun keyword, anonymous
function arguments must be typed:
The return value of an anonymous function is the last expression inside the block. Therefore, the return type of an anonymous function can be inferred—and must be, since there is no way to specify it.
return inside a anonymous function, you need to label the block and then
use a qualified
Anonymous functions have full access to variables declared in the scope in which it was created. This is known as a function closure. Those variables can be read or written, and their values can change.
Note that the closure is created when the anonymous function is created, not when it is run. So variables included in the closure have to be declared before the anonymous function. Meaning that things like this do not work:
Like variables, Kotlin can infer the types of variables that store functions. Meaning that we don’t need to type them explicitly:
Kotlin will check the types of variables that store anonymous functions to ensure that they don’t change:
In some cases function variables can seem to change type. For example, consider the following code:
In this case Kotlin will preserve the initial type of
greeter as not returning
a value (returning
Unit), even after it is reassigned to an anonymous function
that returns a
In contrast, the following will not work:
Kotlin allows us to define the type of a variable refering to an anonymous function, which comprises the type and number of arguments and the return type. This is needed when we define higher-order functions—functions that accept functions as inputs or return them as results—since Kotlin does not infer named function parameter types.
In the example above, the
timeFunction function accepts a single argument: a
method that takes no parameters and returns nothing (
main method also shows the
:: syntax used to pass a reference to a named
function to a higher-order function that expects an anonymous function.
In contrast, in the code above
timeFunction requires a function argument that
takes a single
Int parameter and returns an
So it cannot be called on
greeting but can be called on
Function type arguments ensure that the higher-order function knows how to call
the function passed as an argument, as shown above.
We can also use function types when declaring variables that store references to anonymous functions:
When provided, function types can be used to infer anonymous function parameter and return types:
When we pass an anonymous function that takes a single argument to a
higher-order function, we do not need to name the single parameter.
It will implicitly take on the name
it within the body of the anonymous
However, you are welcome to name the parameter if it is more clear:
When the last parameter to a function is another function, Kotlin allows the function parameter to be moved outside of the paretheses surrounding the other arguments. An example is probably best here:
If the function takes only a single argument, the parentheses can be discarded entirely:
This may seem like a minor feature, until you see how beautiful it renders map-reduce-filter pipelines:
Finally, as you might expect by this point Kotlin functions can contain definitions of other functions, known as local functions. This can be useful to refactor functions when parts of the function need the same functionality but that functionality is not needed elsewhere:
Just like in Java, Kotlin classes combine state and behavior. But unlike Java, Kotlin provides us with an elegant and powerful way to add behavior to exiting classes without involving subclassing or inheritance.
For example, imagine that we want to add a method to a
String that returns
true if the
String looks like a valid
@illinois.edu email address.
Maybe for new we are just going to check that it ends in
eventually we might want to make the validation more sophisticated.
In Java we would do something like this:
We’d then need to attached
validateEmail as a
static method to some library
to use it, and then need to import that library other places in our code.
Not the most elegant solution.
What would be elegant is if we could make
validateEmail appear like a member
String class, but without modifying
In this case
String can’t be subclassed anyway, since it’s
final in Java.
But subclassing and creating a whole new class seems like a lot of effort to go
through to just add one bit of functionality.
Happily, Kotlin provides so-called extension functions:
How cool is that?
validateEmail looks like it’s a member of the
We even have access to the
this within the extension method.
However, behind the scenes what is happening is very similar to what we would need to do in Java. Extension methods do not modify the class that they extend. Instead, they simply provide a more elegant way of making static method calls. The official Kotlin documentation on extension functions has a good rundown of their limitations. But used correctly they can lead to much more elegant and joyful Kotlin code.