Idiomatic Kotlin: Extension functions
This new series is about how to use Kotlin efficiently and learning some of the underrated features of Kotlin. All the articles can be found at the end.
In this article, we will talk about extension functions and its interoperability with Java. Let’s get started.
What is an extension function?
An extension function is function that can be “added” to an existing type without needing to derive a class from it. It can be called as a member of the class even though it is defined outside of it. It is not a new concept as some popular programming languages like C# and Swift already support it.
Motivation
One of the motivations of extension functions is seemless integration with legacy code. Imagine if you can extend a class from your third-party library without actually inheriting it, and not breaking encapsulation at the same time, you can eliminate much of the boilerplate code and unnecessary derived classes.
How can I define an extension function?
Let us take a look at an example and work our way from there.
This is a convenience function to get the second character of a string if it is available. Now let us analyze the syntax. The String
right after the fun
keyword is called the receiver type
. This is the type that you want to extend. The function name follows the type after a .
. Now, the receiver object
can be referenced inside the function using this
. The this
is implicit. You can omit it when calling the object methods and properties inside the extension function body. Note that you can only call public methods and properties of the receiver object. This is what keeps encapsulation intact. The reason why will be discussed later.
Referencing extension functions
Extensions, like any other functions, need to be imported (or in the same package) to be able to use. If for example the above code is added in `extensions` package, you have to import it like
import extensions.second
And use it like
val string = "12"
print(string.second())
Extension function under the hood
Let’s take a look at the decompiled version of our function above.
Under the hood, our extension function is actually a static function with the first parameter as the receiver object. It is enclosed in a class with name generated as the filename appended with kt
(in my case, the filename is ExtensionFunctions). Knowing that it is converted as a static function, it doesn’t add any overhead in terms of performance. And it also explains why we can only access public methods and properties of the receiver object.
Referencing an extension function from Java can therefore be possible. You just have to know the filename and the method name. In our case it can be invoked by
char second = ExtenstionFunctionsKt.second(12);
Inheritance and Polymorphism
Extension methods are not inherited and cannot be overriden. Let’s take a look at why.
Consider two classes with one class inheriting the other one.
If we instantiate a Derived
object and assign it to a Base
variable and print getString
, we will get Derived, which is expected as the method is resolved in runtime. What happens if we throw extension function to the mix?
That’s right, the classes and their relationship will be unaffected. This is because extension functions are static methods outside of the class. To help us visualize more, let us define an extension function for both classes.
The decompiled code for the above extension functions would be
They are static methods and are resolved at compile time. Therefore, calling them via super
will not work (let alone compile). Overriding is just not possible as Kotlin resolves them statically.
Bonus: Extension Properties
Extending properties is also a thing. However, they do not really extend the fields of Java objects. In fact, extension objects don’t even have a backing field at all. That is why it is mandatory for you to implement at least a getter. Let’s modify our function as a property instead.
Calling this function is straightforward, just use property access syntax. In java, it is converted to a getter function like this:
ExtensionFunctionsKt.getSecond("12");
That’s it for extension functions and properties. This idiomatic Kotlin series will be updated with more topics so always stay tuned.
Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.
- Extension Functions
- Sealed Classes
- Infix Functions
- Class Delegation
- Local functions
- Object and Singleton
- Sequences
- Lambdas and SAM constructors
- Lambdas with Receiver and DSL
- Elvis operator
- Property Delegates and Lazy
- Higher-order functions and Function Types
- Inline functions
- Lambdas and Control Flows
- Reified Parameters
- Noinline and Crossinline
- Variance
- Annotations and Reflection
- Annotation Processor and Code Generation