How to Write Custom Extension Functions in Kotlin

Posted on 2025-12-24 by Burak Hamdi TUFAN
General Programming
How to Write Custom Extension Functions in Kotlin
Hello everyone,In this article, we’ll discover the extension functions and how they work under the hood. Also we will create our own extensions.

Let's get started!

What Are Extension Functions?

An extension function allows you to add new methods to existing classes, including classes you don’t own (like standard library types or third-party APIs). They provide better readability with maintaining full type safety.

For example, here toUpperCase is a built-in function of Strings:

val text = "hello world"
val upper = text.toUpperCase()
But like below you can as well define your own custom function like below:

fun String.capitalizeWords(): String {
    return this.split(" ").joinToString(" ") { word ->
        word.replaceFirstChar { it.uppercase() }
    }
}

val sentence = "kotlin extension functions rock!".capitalizeWords()
println(sentence)
// Output: Kotlin Extension Functions Rock!

Under the hood:
The compiler translates extension functions into regular static methods that take the receiver (this) as their first argument and allows you to call them as if they were part of the class.

Here is the breakdown of how it works:

Definition Syntax:

fun <ReceiverType>.functionName(parameters): ReturnType {
    // body using 'this' to refer to receiver
}
Definition Example

fun Int.isEven(): Boolean {
    return this % 2 == 0
}
  • ReceiverType → Int
  • functionName → isEven
  • this → you can access the calling object through.
Usage Example

println(4.isEven()) // true
println(5.isEven()) // false
This is more expressive feature that Kotlin offers then Java.

Now, Let's make examples of extending Standard Library Classes

Let's define some String extensions:

fun String.removeSpaces(): String = this.replace(" ", "")
fun String.wordCount(): Int = this.trim().split(Regex("\\s+")).size

// And use these extensions:
val quote = "  Kotlin makes coding joyful  "
println(quote.removeSpaces()) // Output: Kotlinmakescodingjoyful
println(quote.wordCount()) // Output: 4
Let's also do some for Lists:

fun List<Int>.average(): Double {
    return if (isEmpty()) 0.0 else this.sum().toDouble() / this.size
}

val numbers = listOf(2, 4, 6, 8)
println(numbers.average()) // Output: 5.0
println(emptyList<Int>().average()) // Output: 0.0

We can also create Extension Functions with Generics

You can make extensions generic by adding type parameters. This makes them more flexible across multiple data types. This can help create expressive, domain-specific Kotlin DSLs or utility layers without polluting the business logic.

Here is the syntax and example:

fun <T> List<T>.takeIfNotEmpty(): List<T>? {
    return if (this.isNotEmpty()) this else null
}

val items = listOf("A", "B", "C")
val empty = emptyList<String>()

println(items.takeIfNotEmpty()) // Output: [A, B, C]
println(empty.takeIfNotEmpty()) // Output: null

Extension Properties

As you can extend functions, we can also define extension properties. They cannot hold state but can return computed values.

Below you can see an example about property extension

val String.isPalindrome: Boolean
    get() = this.equals(this.reversed(), ignoreCase = true)

println("Level".isPalindrome) // true
println("Kotlin".isPalindrome) // false

Nullable Receiver Extensions

You can make extensions on nullable receivers, helping you work safely with null. Below example extension handles potential null values gracefully, without forcing repeated null checks.


fun String?.orDefault(default: String = "Unknown"): String {
    return this ?: default
}

val name: String? = null
println(name.orDefault()) // Output: Unknown

Scoping and Importing Extensions

Extension functions can be placed in:
  • Top-level (recommended for utility functions)
  • Inside classes or objects
For instance, if you define them in a separate file like StringExtensions.kt, you can import them where needed like below:
com.thecodeprogram.utils.StringExtensions.kt file:

fun String.capitalizeWords(): String {
    return this.split(" ").joinToString(" ") { word ->
        word.replaceFirstChar { it.uppercase() }
    }
}
Any file that you want to use the extension:

// ...
import com.thecodeprogram.utils.capitalizeWords
// ...
// ...
val data = "the code program".capitalizeWords()

This way, your extensions act as pluggable modules that enhance standard APIs across your project.

Best Practices for Writing Extension Functions

  • Keep them small and focused — perform a single clearly defined task.
  • Prefer them for utility or domain-specific actions, not for major logic or state changes.
  • Organize extensions by domain like StringExtensions.kt, CollectionExtensions.kt, DateExtensions.kt... etc...
  • Document them clearly since they won’t appear directly in class source code.

Extension functions are a efficient way to:

  • Simplify API usage
  • Improve readability
  • Encapsulate reusable logic
  • Extend classes without inheritance or modification

That is All.

Burak Hamdi TUFAN


Tags
Share this Post
Send with Whatsapp