Instruction
What Are Kotlin Extension Functions?
Extension functions are a simple yet powerful concept. They add functionality to a class without modifying its source code or inheriting from it.
Consider a simple String class in Kotlin. Suppose you want to add a method to determine if the string contains an a.
There are a few ways you could do this. Some people would write a StringUtils.kt file and add something like this to the StringUtils class:
fun containsA(input: String): Boolean {
return 'a' in input || 'A' in input
}
Anyone looking to check for the presence of a in the string could then call StringUtils.containsA() and get back a boolean indicating whether a is present.
Alternatively, you could create a wrapper for String. You could have something like MyString, including everything a String contains and your new method.
Your new class might look something like this:
class MyString(private val value: String) {
fun containsA(): Boolean {
return 'a' in value || 'A' in value
}
override fun toString(): String {
return value
}
}
If you were to develop your app like that, you would need to use MyString instead of String everywhere in your app. If you’re a solo app developer, this might be ok since you’ll be able to remember to use MyString.
However, this could cause problems if you’re developing on a team, especially if some members start to use a regular String. Then, keeping all the types aligned could be complicated.
To address the suboptimality of the above scenarios, Kotlin’s developers created Kotlin extension functions. You don’t generate a util or inherit from the base class with these functions.
Instead, you declare your function, and it extends the base class itself. Once you do so, String.containsA() exists for everyone. You and your colleagues can use it like this:
val myString: String
if (myString.containsA()) {
// do something
}
That’s significantly cleaner and easier to write and debug.
##Declaring a Kotlin Extension Function
As with most things in Kotlin, extension functions are straightforward to declare. All you need to do is specify the class you want to extend, follow that with a dot, and the function name you want to create. Then, you can add the body.
Continuing with the example above, all you need to do is declare your extension function like this:
fun String.containsA(): Boolean {
return this.contains('a', ignoreCase = true)
}
Anywhere you use a String in your Kotlin app, it will also have the containsA() function.
One key concept to explore with Kotlin extension functions is the keyword this. Much like in Java, this within one of these extension functions refers to the actual instance of the object on which the function is acting. So, if you have a String declared with contents abc, the this keyword acts on that string abc like so:
val myString = "abc"
myString.containsA()
// This calls...
fun String.containsA(): Noolean {
// In the code below, *this* refers to myString.
return this.contains('a', ignoreCase = true)
}
Accessing the contents of the variable or class directly makes it much easier to work with the underlying data and results in cleaner code. You no longer have to do something where you have a utility function that does something like this:
class StringUtils() {
fun containsA(input: string) {
// Logic
}
}
if (StringUtils.containsA(myString)) {
// do something
}
Instead, you can simplify that into something much more logical:
if (myString.containsA()) {
// do that same thing
}
As you can see, Kotlin functions are straightforward to use and provide excellent value to developers.
What Are Kotlin Extension Properties?
Kotlin extension properties help you add new features to an existing class without making complicated changes to its structure, making your code more flexible and easier to understand.
For example, imagine you have a box called FruitBox but want to add a new property called totalCost. Instead of modifying the FruitBox class directly, you can use extension properties to add this new property to FruitBox objects whenever you need it.
##Declaring a Kotlin Extension Properties
You define an extension property by starting with either val or var, followed by the type you’re extending, a dot ., the name of your property, and finally, the type of your property. You then define the logic for getting the property’s value using the get() function. For mutable properties, you can also define a set() function to update the property’s value.
Here’s a slight modification to the extension function code to demonstrate the extension properties:
val String.containsA: Boolean
get() = this.contains("A", ignoreCase = true)
Here’s a code breakdown:
-
valindicates thatcontainsAis a read-only property. -
Stringspecifies the receiver type for the extension property, which isStringin this case. -
containsAis the name of the extension property. -
get()is the getter function that computes the property’s value. -
this.contains("A", ignoreCase = true)determines if the string contains the letterA(case-insensitive).
##Practical Uses for Kotlin Extension Functions
So far, you’ve been dealing with toy examples. While a containsA() function may have some uses, most real-world use cases are slightly more complex. Here are some real-world examples of when you may want to use these functions.
###Dates and Times
Consider the following common problem: You get a date and time back from your API server in UTC format, but you want to render it to the user, formatted as they expect, and convert it to their timezone.
You could have an extension function that looks something like this:
fun Date.toReadableString(): String {
// Implementation to return a nicely formatted date string
}
That way, whenever you want to display the date and time to the user, you need to call this function, which will give them the date and time the same way. It’s not hard to see how such an extension function is usable across multiple list views and tables.
The nice thing about doing the date and time in this format is that you can always swap out the logic if you need to change it in one location. Once you do that, everything else will automatically update. Plus, it’s pretty easily testable.
Binding Data
RecyclerViews in Android can be a bit repetitive and cluttered in terms of code. Sometimes, you can encapsulate that repetitiveness in an extension function. Encapsulating the logic of one of these functions will make your adapters cleaner and more concise.
fun View.bindData(item: DataItem) {
// Bind data to the view
}
Much like the use case above, doing the bindings this way ensures that the central logic for this piece lives in one section. Once you change that one function, you’ve changed the logic everywhere.
As you can see, Kotlin extension functions are compelling. While they help with many use cases, they primarily solve the issue of centralization; they help consolidate logic and make it easier for developers to write and maintain. Given their power, you might think they’re complex under the hood. Spoiler alert: they’re not!
In reality, Kotlin functions are simple. They’re primarily syntactic sugar. They take the receiver object, the class you’re extending, as a parameter.
Recall the this keyword. Under the hood, this is the parameter, so it appears you’re calling a method that’s part of the class itself.
Consider this Kotlin extension function:
fun String.isGreaterThanThreeCharacters(): Boolean = this.length > 3
Under the hood, this is essentially equivalent to a standard utility function you may have written before:
fun isStringGreaterThanThreeCharacters(str: String): Boolean = str.length > 3
Of course, the developers of Kotlin have abstracted all complexity away from you, the writer. All you see is the code in the first block, making your life infinitely easier!