Kotlin Help

Functions

You can declare your own functions in Kotlin using the fun keyword.

fun hello() { return println("Hello, world!") } fun main() { hello() // Hello, world! }

In Kotlin:

  • Function parameters are written within parentheses ().

  • Each parameter must have a type, and multiple parameters must be separated by commas ,.

  • The return type is written after the function's parentheses (), separated by a colon :.

  • The body of a function is written within curly braces {}.

  • The return keyword is used to exit or return something from a function.

In the following example:

  • x and y are function parameters.

  • x and y have type Int.

  • The function's return type is Int.

  • The function returns a sum of x and y when called.

fun sum(x: Int, y: Int): Int { return x + y } fun main() { println(sum(1, 2)) // 3 }

Named arguments

For concise code, when calling your function, you don't have to include parameter names. However, including parameter names does make your code easier to read. This is called using named arguments. If you do include parameter names, then you can write the parameters in any order.

fun printMessageWithPrefix(message: String, prefix: String) { println("[$prefix] $message") } fun main() { // Uses named arguments with swapped parameter order printMessageWithPrefix(prefix = "Log", message = "Hello") // [Log] Hello }

Default parameter values

You can define default values for your function parameters. Any parameter with a default value can be omitted when calling your function. To declare a default value, use the assignment operator = after the type:

fun printMessageWithPrefix(message: String, prefix: String = "Info") { println("[$prefix] $message") } fun main() { // Function called with both parameters printMessageWithPrefix("Hello", "Log") // [Log] Hello // Function called only with message parameter printMessageWithPrefix("Hello") // [Info] Hello printMessageWithPrefix(prefix = "Log", message = "Hello") // [Log] Hello }

Functions without return

If your function doesn't return a useful value then its return type is Unit. Unit is a type with only one value – Unit. You don't have to declare that Unit is returned explicitly in your function body. This means that you don't have to use the return keyword or declare a return type:

fun printMessage(message: String) { println(message) // `return Unit` or `return` is optional } fun main() { printMessage("Hello") // Hello }

Single-expression functions

To make your code more concise, you can use single-expression functions. For example, the sum() function can be shortened:

fun sum(x: Int, y: Int): Int { return x + y } fun main() { println(sum(1, 2)) // 3 }

You can remove the curly braces {} and declare the function body using the assignment operator =. When you use the assignment operator =, Kotlin uses type inference, so you can also omit the return type. The sum() function then becomes one line:

fun sum(x: Int, y: Int) = x + y fun main() { println(sum(1, 2)) // 3 }

However, if you want your code to be quickly understood by other developers, it's a good idea to explicitly define the return type even when using the assignment operator =.

Early returns in functions

To stop the code in your function from being processed further than a certain point, use the return keyword. This example uses if to return from a function early if the conditional expression is found to be true:

// A list of registered usernames val registeredUsernames = mutableListOf("john_doe", "jane_smith") // A list of registered emails val registeredEmails = mutableListOf("john@example.com", "jane@example.com") fun registerUser(username: String, email: String): String { // Early return if the username is already taken if (username in registeredUsernames) { return "Username already taken. Please choose a different username." } // Early return if the email is already registered if (email in registeredEmails) { return "Email already registered. Please use a different email." } // Proceed with the registration if the username and email are not taken registeredUsernames.add(username) registeredEmails.add(email) return "User registered successfully: $username" } fun main() { println(registerUser("john_doe", "newjohn@example.com")) // Username already taken. Please choose a different username. println(registerUser("new_user", "newuser@example.com")) // User registered successfully: new_user }

Functions practice

Exercise 1

Write a function called circleArea that takes the radius of a circle in integer format as a parameter and outputs the area of that circle.

import kotlin.math.PI // Write your code here fun main() { println(circleArea(2)) }
import kotlin.math.PI fun circleArea(radius: Int): Double { return PI * radius * radius } fun main() { println(circleArea(2)) // 12.566370614359172 }

Exercise 2

Rewrite the circleArea function from the previous exercise as a single-expression function.

import kotlin.math.PI // Write your code here fun main() { println(circleArea(2)) }
import kotlin.math.PI fun circleArea(radius: Int): Double = PI * radius * radius fun main() { println(circleArea(2)) // 12.566370614359172 }

Exercise 3

You have a function that translates a time interval given in hours, minutes, and seconds into seconds. In most cases, you need to pass only one or two function parameters while the rest are equal to 0. Improve the function and the code that calls it by using default parameter values and named arguments so that the code is easier to read.

fun intervalInSeconds(hours: Int, minutes: Int, seconds: Int) = ((hours * 60) + minutes) * 60 + seconds fun main() { println(intervalInSeconds(1, 20, 15)) println(intervalInSeconds(0, 1, 25)) println(intervalInSeconds(2, 0, 0)) println(intervalInSeconds(0, 10, 0)) println(intervalInSeconds(1, 0, 1)) }
fun intervalInSeconds(hours: Int = 0, minutes: Int = 0, seconds: Int = 0) = ((hours * 60) + minutes) * 60 + seconds fun main() { println(intervalInSeconds(1, 20, 15)) println(intervalInSeconds(minutes = 1, seconds = 25)) println(intervalInSeconds(hours = 2)) println(intervalInSeconds(minutes = 10)) println(intervalInSeconds(hours = 1, seconds = 1)) }

Lambda expressions

Kotlin allows you to write even more concise code for functions by using lambda expressions.

For example, the following uppercaseString() function:

fun uppercaseString(text: String): String { return text.uppercase() } fun main() { println(uppercaseString("hello")) // HELLO }

Can also be written as a lambda expression:

fun main() { val upperCaseString = { text: String -> text.uppercase() } println(upperCaseString("hello")) // HELLO }

Lambda expressions can be hard to understand at first glance so let's break it down. Lambda expressions are written within curly braces {}.

Within the lambda expression, you write:

  • The parameters followed by an ->.

  • The function body after the ->.

In the previous example:

  • text is a function parameter.

  • text has type String.

  • The function returns the result of the .uppercase() function called on text.

  • The entire lambda expression is assigned to the upperCaseString variable with the assignment operator =.

  • The lambda expression is called by using the variable upperCaseString like a function and the string "hello" as a parameter.

  • The println() function prints the result.

Lambda expressions can be used in a number of ways. You can:

Pass to another function

A great example of when it is useful to pass a lambda expression to a function, is using the .filter() function on collections:

fun main() { //sampleStart val numbers = listOf(1, -2, 3, -4, 5, -6) val positives = numbers.filter ({ x -> x > 0 }) val isNegative = { x: Int -> x < 0 } val negatives = numbers.filter(isNegative) println(positives) // [1, 3, 5] println(negatives) // [-2, -4, -6] //sampleEnd }

The .filter() function accepts a lambda expression as a predicate:

  • { x -> x > 0 } takes each element of the list and returns only those that are positive.

  • { x -> x < 0 } takes each element of the list and returns only those that are negative.

This example demonstrates two ways of passing a lambda expression to a function:

  • For positive numbers, the example adds the lambda expression directly in the .filter() function.

  • For negative numbers, the example assigns the lambda expression to the isNegative variable. Then the isNegative variable is used as a function parameter in the .filter() function. In this case, you have to specify the type of function parameters (x) in the lambda expression.

Another good example, is using the .map() function to transform items in a collection:

fun main() { //sampleStart val numbers = listOf(1, -2, 3, -4, 5, -6) val doubled = numbers.map { x -> x * 2 } val isTripled = { x: Int -> x * 3 } val tripled = numbers.map(isTripled) println(doubled) // [2, -4, 6, -8, 10, -12] println(tripled) // [3, -6, 9, -12, 15, -18] //sampleEnd }

The .map() function accepts a lambda expression as a transform function:

  • { x -> x * 2 } takes each element of the list and returns that element multiplied by 2.

  • { x -> x * 3 } takes each element of the list and returns that element multiplied by 3.

Function types

Before you can return a lambda expression from a function, you first need to understand function types.

You have already learned about basic types but functions themselves also have a type. Kotlin's type inference can infer a function's type from the parameter type. But there may be times when you need to explicitly specify the function type. The compiler needs the function type so that it knows what is and isn't allowed for that function.

The syntax for a function type has:

  • Each parameter's type written within parentheses () and separated by commas ,.

  • The return type written after ->.

For example: (String) -> String or (Int, Int) -> Int.

This is what a lambda expression looks like if a function type for upperCaseString() is defined:

val upperCaseString: (String) -> String = { text -> text.uppercase() } fun main() { println(upperCaseString("hello")) // HELLO }

If your lambda expression has no parameters then the parentheses () are left empty. For example: () -> Unit

Return from a function

Lambda expressions can be returned from a function. So that the compiler understands what type the lambda expression returned is, you must declare a function type.

In the following example, the toSeconds() function has function type (Int) -> Int because it always returns a lambda expression that takes a parameter of type Int and returns an Int value.

This example uses a when expression to determine which lambda expression is returned when toSeconds() is called:

fun toSeconds(time: String): (Int) -> Int = when (time) { "hour" -> { value -> value * 60 * 60 } "minute" -> { value -> value * 60 } "second" -> { value -> value } else -> { value -> value } } fun main() { val timesInMinutes = listOf(2, 10, 15, 1) val min2sec = toSeconds("minute") val totalTimeInSeconds = timesInMinutes.map(min2sec).sum() println("Total time is $totalTimeInSeconds secs") // Total time is 1680 secs }

Invoke separately

Lambda expressions can be invoked on their own by adding parentheses () after the curly braces {} and including any parameters within the parentheses:

fun main() { //sampleStart println({ text: String -> text.uppercase() }("hello")) // HELLO //sampleEnd }

Trailing lambdas

As you have already seen, if a lambda expression is the only function parameter, you can drop the function parentheses (). If a lambda expression is passed as the last parameter of a function, then the expression can be written outside the function parentheses (). In both cases, this syntax is called a trailing lambda.

For example, the .fold() function accepts an initial value and an operation:

fun main() { //sampleStart // The initial value is zero. // The operation sums the initial value with every item in the list cumulatively. println(listOf(1, 2, 3).fold(0, { x, item -> x + item })) // 6 // Alternatively, in the form of a trailing lambda println(listOf(1, 2, 3).fold(0) { x, item -> x + item }) // 6 //sampleEnd }

For more information on lambda expressions, see Lambda expressions and anonymous functions.

The next step in our tour is to learn about classes in Kotlin.

Lambda expressions practice

Exercise 1

You have a list of actions supported by a web service, a common prefix for all requests, and an ID of a particular resource. To request an action title over the resource with ID: 5, you need to create the following URL: https://example.com/book-info/5/title. Use a lambda expression to create a list of URLs from the list of actions.

fun main() { val actions = listOf("title", "year", "author") val prefix = "https://example.com/book-info" val id = 5 val urls = // Write your code here println(urls) }
fun main() { val actions = listOf("title", "year", "author") val prefix = "https://example.com/book-info" val id = 5 val urls = actions.map { action -> "$prefix/$id/$action" } println(urls) }

Exercise 2

Write a function that takes an Int value and an action (a function with type () -> Unit) which then repeats the action the given number of times. Then use this function to print “Hello” 5 times.

fun repeatN(n: Int, action: () -> Unit) { // Write your code here } fun main() { // Write your code here }
fun repeatN(n: Int, action: () -> Unit) { for (i in 1..n) { action() } } fun main() { repeatN(5) { println("Hello") } }

Next step

Classes

Last modified: 22 October 2024