In the previous article I talked about what exactly makes Kotlin so great (hint: it's not a single killer feature but a lot of different useful features) and how you would deal with Java's nullability issues in Kotlin. In this article we're diving deeper into the Kotlin world and taking a look at extension functions. What are they, when are they used and which problems do they solve?
Class Envy
Have you ever wished an internal Java class had an extra super useful method, for instance java.lang.String
so you could call it directly on any String? Imagine you had a method which reverses a String, how cool would it be to be able to call "hello world".reverse()
? In Java this is impossible, unless you can convince the Java developers to add it to the java.lang.String
class for you...
Static Utility Methods
The current way this problem is solved in Java is with static utility classes and methods. You might have a StringUtils
class which has a method public static String reverse(String s)
. You would call it with StringUtils.reverse(s)
and of course this works just as well, but it doesn't feel as natural as being able to simply call s.reverse()
.
Extension Functions
Kotlin introduces extension functions to solve this problem. To replace our static Java method, we would write it like so in Kotlin.
fun String.reverse(): String {
// implementation left up to the reader
}
And you could call it on any String variable or literal: myString.reverse()
and "hello".reverse()
are both allowed.
Extension functions become really helpful when you have more than 1 variable, imagine you have a static Java method with the following signature: public static boolean isDivisibleBy(int i, int j)
and in your code you call it with boolean isDivisible = IntUtils.isDivisibleBy(2, 4)
. It's not easy to discern whether this methods checks if 2 is divisible by 4 or the other way around. Which one is the numerator and which one is the denominator?
With Kotlin, it becomes easy and straightforward.
// 'this' refers to the object the function is called on
fun Int.isDivisibleBy(other: Int) = this % other == 0
// Now we can do this
4.isDivisibleBy(2)
This looks a lot better! But wait, there's more!
Infix Functions
There is a special modifier for Kotlin functions, infix
, which removes the need for the caller to add a dot and parantheses, making it slightly more readable. Let's add the infix keyword to our extension function.
infix fun Int.isDivisibleBy(other: Int) = this % other == 0
// Look, mommy, no extra characters
4 isDivisibleBy 2
// You could still do this, which one you use is a matter of preference
4.isDivisibleBy(2)
The Kotlin Standard Library
Kotlin loves extension functions so much that they have added a LOT of them to the standard library themselves. The reverse
method we talked about actually already exists in Kotlin (it's called reversed()
). Most of the often used Java classes such as String, numbers and collections have a lot of useful extension functions which make working with them in Kotlin a joy. And if you need an extension function which doesn't already exist, you can simply add it in your own code.
Summary
In this article we looked at extension functions and how they provide improved usability over Java's static class and method equivalent. Extension functions help us write our code in a more natural way. The infix modifier allows our functions to look even more natural to the callers. Extension functions also allow us to not get confused when a method has different parameters of the same type.