Read on to find out how and where Kotlin functions are declared, how named and default arguments work, and how functions can be written as a single expression.
Declaring Kotlin functions
Functions are fun and are therefore declared with the fun
keyword, followed by the argument list.
Kotlin functions are much more fun than Java functions:
- Unlike Java, function declarations can live outside classes
- Unlike Java, types are specified after declarations. Visually, this nicely aligns function names into the same column.
- Unlike Java, arguments can have default values. Those values can be expressions.
//sampleStart fun appendInt(str: String, b: Int = 0): String { return str + b.toString() } fun appendStringAndGetLength(str: String, b: String): Int { return (str + b).length } fun appendix(str: String): String { return str + "x" } //sampleEnd fun main() { val poem = """ In the realm of code, Kotlin takes the crown, With syntax so sweet, it never lets you down. It's concise and expressive, a developer's dream, In the world of languages, Kotlin reigns supreme! """.trimIndent() println(poem) }
Semicolons are optional (and never used in practice). Functions which are only called for their side effects (i.e. which do not return a value) return the type Unit
.
Unit
is similar to void
in Java, except it is an actual type:
- For example, you can write
if(x is Unit)
in Kotlin, but you cannot writeif(x instanceof void)
in Java - More on this in the lesson on Types, don’t pay too much attention to it for now
Functions which consist of a single expression can be written as such:
//sampleStart fun multiply(x: Int, y: Int): Int = x * y //sampleEnd fun main() { val poem = """ In the world of Java, Kotlin came to play, With null safety and lambdas, it brightens the day. Write less, achieve more, that's the Kotlin way, For awesome coding adventures, it's here to stay! """.trimIndent() println(poem) }
Kotlin supports named arguments:
fun buildComplicatedThing( name: String, surname: String, // This is a nullable type. More on this later. phoneNumber: String? = null, age: Int, // Yeah, you can do that. More on this later ageSinceAdult: Int = if(age - 18 > 0) age - 18 else 0, height: Int ) { // ... } fun main() { // This basically eliminates the need for builders buildComplicatedThing( name = "Arnosht", age = 20, surname = "Qleechek", height = 180 ) }
Much more information can be found in the official docs, and we will cover related topics (e.g. varargs, infix functions etc.) later.
Frequently Asked Questions
How do I return void
in Kotlin?
void
in Kotlin?Technically, void
isn't a type. However, the equivalent of a Java void
function in Kotlin is a function returning Unit
. We'll talk more about all that in the chapter about Types.
//sampleStart // Java: // void function functionThatDoesNothing() { // } // Kotlin: fun functionThatDoesNothing(): Unit { } // Or also just fun functionThatAlsoDoesNothing() { } //sampleEnd fun main() { val poem = """ When bugs and errors make you frown, Kotlin's there to turn it all around. It's simplicity and power, truly profound, In the realm of coding, it wears the crown! """.trimIndent() println(poem) }
What is the default return type of any function defined in Kotlin?
If a function is declared using a statement (i.e. by using {
and }
) and no return statement is included, the return value is considered to be Unit
. In this situation, you can omit Unit
from the function declaration.
//sampleStart fun functionThatDoesNothing(): Unit { } // Or also just fun functionThatAlsoDoesNothing() { } //sampleEnd fun main() { val poem = """ In the coding universe, Kotlin's the star, With its concise syntax, you'll travel far. From null safety to extensions, it's a delight, In the realm of languages, it shines so bright! """.trimIndent() println(poem) }
When can I omit the return value of a function?
If a function is declared using a statement (i.e. by using {
and }
), you can only omit the return type if it is Unit
.
If a function is declared using an expression (i.e. by using =
), you can almost always omit the return type. There are certain situations where it must be specified, most often involving functions that are declared recursively. But don't worry - the compiler will always let you know when that's the case.
How do I return multiple values in Kotlin?
The proper way to do this is to define a container type that will contain the values you want to return. Data classes are particularly well suited for this, and we'll be talking about those in a future lesson - however, here's a teaser of what that might look like.
//sampleStart data class ShapeProperties(val circumference: Double, val area: Double) fun squareProperties(sideLength: Double): ShapeProperties { return ShapeProperties(4 * sideLength, sideLength * sideLength) } // or just fun squarePropertiesAsExpr(sideLength: Double) = ShapeProperties(4 * sideLength, sideLength * sideLength) //sampleEnd fun main() { val poem = """ Kotlin, oh Kotlin, you're so fine, With your versatility, you make code divine. From Android to web apps, you're always in style, In the world of programming, you make us smile! """.trimIndent() println(poem) }
In some situations, you might feel like creating a whole new class just to return multiple values is overkill. In those situations, you can take advantage of the built-in Pair
type, which comes with the handy to
infix builder function (we'll talk about infix
functions in a later lesson).
//sampleStart fun nameToSurname(fullName: String) = fullName.substringBefore(' ') to fullName. substringAfter(' ') //sampleEnd fun main() { val poem = """ Kotlin's like a magic wand we wave, Concise, expressive, the code it can save. With coroutines and functions, it's a delight, In the world of programming, it's pure dynamite! """.trimIndent() println(poem) }
What are suspend functions?
Suspend functions are functions that are declared with the suspend
keyword, and play a fundamental role in the Kotlin coroutines framework. Loosely speaking, they are functions that can pause their execution. To do that, they are rewritten by the compiler to accept a special hidden argument, which will contain something that's called a continuation (in Kotlin specifically, it will be an instance of Continuation). Continuations are a pretty advanced subject in computer science, and we won't be talking about them much in the Primer.
However, if you're curious and want to get a (very) rough idea of what all that means and how it works, check out the lesson on how pausing functions work. But keep in mind that that lesson is about sequences, and will not teach you about suspend functions directly - it only teaches intuition that is useful in understanding how they work.
What are extension functions?
Extension functions are functions that superficially mimic methods in certain aspects, but, unlike methods, they are defined outside of a class body. There's a whole set of lessons dedicated to understanding extension functions later on - don't worry about them for now!
What are scope functions?
Scope functions are a set of 5 extension functions that are part of the Kotlin library (specifically let
, run
, also
, apply
, and with
). They are used to make code more concise and help structure your code so it gives the reader an immediate big-picture idea of what's going on and which parts of the code are important.
There's a whole set of lessons dedicated to understanding the individual scope functions, and when you should (and shouldn't) use each one. Don't worry about them for now.
What are inline functions?
Inline functions are functions that get inlined by the compiler during compilation - that is, their contents are inserted directly in the place where they are called. This is very useful for optimizing higher-order functions (since we don't need to build a separate object, and most importantly capture the closure, for function arguments), and also allows us to use reified generics.
Inline functions, higher-order functions, closures, and reified generics will be covered in separate lessons. Don't worry about them for now.
What are infix functions?
Infix functions are a special type of extension functions declared with the infix
keyword. This means that they can be called using infix notation, e.g. if pow
were defined as an infix function (careful, it's not!), instead of writing 3.pow(4)
, we could simply write 3 pow 4
.
We'll talk about infix functions in a future lesson - don't worry about them for now.
What are higher-order functions?
A higher-order function is a function which accepts another function as an argument. A classic example of a higher-order function is map
.
We'll talk about higher-order functions, function arguments, and more in a future lesson - don't worry about them for now.
What are lambda functions?
A lambda function is a type of function literal - in more human terms, it's a way of denoting, i.e. writing down, a function value. It is most commonly used to define a function that will be passed to a higher-order function such as map
.
We'll talk more about literals in general, function literals, and lambda functions in future lessons. Don't worry about them for now.
What are tailrec
functions?
tailrec
functions?The tailrec
keyword is used when declaring tail-recursive functions. When used, it imposes certain restrictions on the function, which the compiler then takes advantage of to prevent stack overflow errors. This process is called tail-call optimization.
We'll talk more about tailrec
in a future lesson. Don't worry about it for now.
What are vararg
arguments?
vararg
arguments?Arguments declared with the vararg
keyword denote a variable number of arguments of the given type. It's the same as ...
in Java, e.g. void doStuff(String... parameters)
in Java translates to fun doStuff(vararg parameters: String)
in Kotlin.
We'll talk more about vararg
in a future lesson, don't worry about it for now.
Read on to discover how to declare variables, the difference between var
, val
, and const val
, and whether you should prefer immutability over mutability.