Kotlin Extensions: A Complete Guide to Extension Functions in Kotlin

Meet Miyani
7 min readAug 26, 2024

--

Let’s explore one of Kotlin’s most underrated features: Extensions! Kotlin allows you to add new functionality to a class or an interface without needing to inherit from it or use design patterns like the Decorator. This is made possible through special declarations known as extensions.

For example, you can create new functions for a class or an interface from a third-party library that you can’t change. These functions can be used just like they were part of the original class. This is known as an extension function. Additionally, Kotlin also allows you to define new properties for existing classes, called extension properties.

What did you just say?

Let’s take an example to better understand Kotlin extensions. Imagine you buy a car that comes with its own set of functions and features. However, you might want something extra, like adding new accessories or boosting its performance. Even though you can’t change the car’s original factory settings, you can still enhance it by adding accessories like guards, upgrading the exhaust system, tuning the engine for more power, or even installing interior lights. These additions improve the car without altering its original structure — just like Kotlin extensions allow you to add new functionality to a class without modifying the original code. In both cases, the core remains untouched, but the added features (or extensions) give you more options and flexibility.

Extensions don’t actually modify the classes they extend. Instead, they allow you to call new functions using dot-notation on variables of that type, without adding new members to the class itself.

Let’s dive into some example to understand better now!

// Base URL for the articles
val BASE_URL = "https://medium.com/@miyanimeet02_57894/"

// Article paths
val SIMPLIFYING_CONSTRAINT_LAYOUT = "simplifying-constraint-layout-constraints-programmatically-91adf32e80f9"
val ANDROID_BUILD_VARIANTS = "android-apk-build-variants-useful-for-qa-testing-87c1b8f1ea61"

// Extension function for the String class
fun String.toArticleUrl(): String {
return BASE_URL + this
}

fun main() {
// Using the extension function to get the full article URL
val fullArticleUrl = SIMPLIFYING_CONSTRAINT_LAYOUT.toArticleUrl()

// Print the original article path
println("Article Path: $SIMPLIFYING_CONSTRAINT_LAYOUT")

// Print the full article URL
println("Full Article URL: $fullArticleUrl")
}

Here, toArticleUrl() is an extension function added to the String class. It allows any String instance to be treated as an article path and, when called, returns the full URL by appending the BASE_URL to the string.

Outputs:

  1. Article Path:
    simplifying-constraint-layout-constraints-programmatically-91adf32e80f9
  2. Full Article URL:
    https://medium.com/@miyanimeet02_57894/simplifying-constraint-layout-constraints-programmatically-91adf32e80f9

Here, the String class itself isn’t modified, but an extension function is created for it. This lets you use the function with the dot operator on any string. The function prepends the string with BASE_URL and returns the result.

For example, if we use it with “meet-miyani”

fun main() {
val url = "meet-miyani".toArticleUrl()
}

This will output:

Let’s break down the extension function we created:

val BASE_URL = "https://medium.com/@miyanimeet02_57894/"

// Extension function for the String class
fun String.toArticleUrl(): String {
return BASE_URL + this
}

In this extension function, we use this to reference the String instance it's called on. Since this function extends the String class, this represents the original string. The function returns a value by prepending the BASE_URL to this.

It’s important to note that extension functions don’t always have to return a value — they can perform actions without returning anything. Here’s an example:

// Extension function for the String class
fun String.printArticleUrl() {
val articleUrl = BASE_URL + this
println(articleUrl)
}

In this version, printArticleUrl() doesn't return anything; it simply constructs the full URL and prints it. This shows the flexibility of extension functions in Kotlin—they can either return a value or just perform an action based on the class they extend.

I hope we’re on the right track, and that you now understand how extensions work in Kotlin.

Now let’s have some real fun with Kotlin Extensions!

Imagine we want to compare two numbers of different data types. Consider this code:

fun main() {
var first: Int? = 5
var second: Double? = 5.01

val condition = first > second
}

This will result in a compiler error because the numbers are of different types and both are nullable. While we could force non-nullability by adding !! at the end of the variables, that's not ideal. Using !! ignores the possibility that these values could be null, which might lead to a crash if they are.

Instead, let’s explore how we can solve this more elegantly with Kotlin extension functions!

fun Number?.isGreaterThan(number: Number?): Boolean {
if (this == null) return false
if (number == null) return true
return this.toDouble() > number.toDouble()
}

fun Number?.isGreaterThanOrEqualTo(number: Number?): Boolean {
if (this == null) return false
if (number == null) return true
return this.toDouble() >= number.toDouble()
}

fun Number?.isSmallerThan(number: Number?): Boolean {
if (this == null) return true
if (number == null) return false
return this.toDouble() < number.toDouble()
}

fun Number?.isSmallerThanOrEqualTo(number: Number?): Boolean {
if (this == null) return true
if (number == null) return false
return this.toDouble() <= number.toDouble()
}

I created these extension functions to handle comparisons between any numeric types — whether Double, Float, Long, or Int. Notice that the Number? type is used, meaning the numbers can be nullable. These functions gracefully handle null values, ensuring that if either number is null, the comparison logic still works as expected.

For example:

  • If this (the calling number) is null, the function returns false for "greater than" or "greater than or equal to" comparisons, and true for "less than" or "less than or equal to" comparisons.
  • If the number parameter is null, the function treats this as greater, or equal, depending on the comparison.

This approach ensures your comparisons are safe and robust, even when working with nullable values.

And to use it, pretty simple!

fun main() {
var first: Int? = 5
var second: Double? = 5.01

val condition = first.isGreaterThan(second)
}

But there’s more! Kotlin is like a concise poem — what if we could write it directly as first isGreaterThan second? This would make the statement even more readable, like plain English, right?

To achieve this, we can use infix notation. By adding the infix keyword before the function declaration, we can call the function without dots or parentheses, making the code cleaner and more natural to read.

Here’s how to modify the function:

infix fun Number?.isGreaterThan(number: Number?): Boolean {
if (this == null) return false
if (number == null) return true
return this.toDouble() > number.toDouble()
}

Now, you can write:

fun main() {
var first: Int? = 5
var second: Double? = 5.01

val condition = first isGreaterThan second
}

There are many situations where this function can be very useful. For example, in Android, you might want to apply or remove a strikethrough on a TextView.

Typically, you would do it like this:

// To set the Strike Through
textView.paintFlags = textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG

// To remove the Strike Through
textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()

However, writing this code every time can be repetitive and messy. Instead, we can create simple extension functions to handle it:

fun TextView.setStrikeThrough() {
paintFlags = paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
}

fun TextView.clearStrikeThrough() {
paintFlags = paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}

Now, you can easily apply or remove a strikethrough with a simple function call, making your code cleaner and more readable.

// To set the Strike Through
textView.setStrikeThrough()

// To remove the Strike Through
textView.clearStrikeThrough()

Now we understand Kotlin Extensions and the power they bring to our code!

Wrapping Up!

Kotlin extensions are a powerful tool that can:

  • Add new functionality to existing classes without modifying their original code.
  • Make your codebase cleaner, more readable, and easier to maintain.
  • Provide a flexible and elegant solution for various coding scenarios.

Now that you have the basics of Kotlin extensions, you can start playing around and creating your own. If you come up with any useful functions, feel free to share them in the comments so other developers can benefit from them too.

If you want to dive deeper into Kotlin extensions, here’s the link to the official Kotlin documentation on extension functions.

Happy coding!

Wait, wait, wait! don’t forget to clap and share if you found this article helpful!

--

--

Meet Miyani

Android Developer | 4 years of Kotlin experience | Enthusiastic learner & passionate about sharing knowledge | Crafting efficient and innovative mobile solution