Kotlin Help

Intermediate: Extension functions

In this chapter, you'll explore special Kotlin functions that make your code more concise and readable. Learn how they can help you use efficient design patterns to take your projects to the next level.

Extension functions

In software development, you often need to modify the behavior of a program without altering the original source code. For example, in your project, you might want to add extra functionality to a class from a third-party library.

Extension functions allow you to extend a class with additional functionality. You call extension functions the same way you call member functions of a class.

Before introducing the syntax for extension functions, you need to understand the terms receiver type and receiver object.

The receiver object is what the function is called on. In other words, the receiver is where or with whom the information is shared.

An example of sender and receiver

In this example, the main() function calls the .first() function. The .first() function is called on the readOnlyShapes variable, so the readOnlyShapes variable is the receiver.

The receiver object has a type so that the compiler understands when the function can be used.

This example uses the .first() function from the standard library to return the first element in a list. To create your own extension function, write the name of the class that you want to extend followed by a . and the name of your function. Continue with the rest of the function declaration, including its arguments and return type.

For example:

fun String.bold(): String = "<b>$this</b>" fun main() { // "hello" is the receiver object println("hello".bold()) // <b>hello</b> }

In this example:

  • String is the extended class, also known as the receiver type.

  • bold is the name of the extension function.

  • The .bold() extension function's return type is String.

  • "hello", an instance of String, is the receiver object.

  • The receiver object is accessed inside the body by the keyword: this.

  • A string template ($) is used to access the value of this.

  • The .bold() extension function takes a string and returns it in a <b> HTML element for bold text.

Extension-oriented design

You can define extension functions anywhere, which enables you to create extension-oriented designs. These designs separate core functionality from useful but non-essential features, making your code easier to read and maintain.

A good example is the HttpClient class from the Ktor library, which helps perform network requests. The core of its functionality is a single function request(), which takes all the information needed for an HTTP request:

class HttpClient { fun request(method: String, url: String, headers: Map<String, String>): HttpResponse { // Network code } }

In practice, the most popular HTTP requests are GET or POST requests. It makes sense for the library to provide shorter names for these common use cases. However, these don't require writing new network code, only a specific request call. In other words, they are perfect candidates to be defined as separate .get() and .post() extension functions:

fun HttpClient.get(url: String): HttpResponse = request("GET", url, emptyMap()) fun HttpClient.post(url: String): HttpResponse = request("POST", url, emptyMap())

These .get() and .post() functions call the request() function with the correct HTTP method, so you don't have to. They streamline your code and make it easier to understand:

class HttpClient { fun request(method: String, url: String, headers: Map<String, String>): HttpResponse { println("Requesting $method to $url with headers: $headers") return HttpResponse("Response from $url") } } fun HttpClient.get(url: String): HttpResponse = request("GET", url, emptyMap()) fun main() { val client = HttpClient() // Making a GET request using request() directly val getResponseWithMember = client.request("GET", "https://example.com", emptyMap()) // Making a GET request using the get() extension function val getResponseWithExtension = client.get("https://example.com") }

This extension-oriented approach is widely used in Kotlin's standard library and other libraries. For example, the String class has many extension functions to help you work with strings.

For more information about extension functions, see Extensions.

Practice

Exercise 1

Write an extension function called isPositive that takes an integer and checks whether it is positive.

fun Int.// Write your code here fun main() { println(1.isPositive()) // true }
fun Int.isPositive(): Boolean = this > 0 fun main() { println(1.isPositive()) // true }

Exercise 2

Write an extension function called toLowercaseString that takes a string and returns a lowercase version.

Hint

Use the .lowercase() function for the String type.

fun // Write your code here fun main() { println("Hello World!".toLowercaseString()) // hello world! }
fun String.toLowercaseString(): String = this.lowercase() fun main() { println("Hello World!".toLowercaseString()) // hello world! }

Next step

Intermediate: Scope functions

Last modified: 28 April 2025