How to Write Custom Extension Functions in Kotlin
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.
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
}
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: 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
Extension functions are a efficient way to:
That is All.
Burak Hamdi TUFAN