Blog / October 26, 2018 / 5 mins read / By Suneet Agrawal

Extensions in Kotlin

Have you ever felt some useful functionality missing in an existing class?

The class could be in the libraries provided by language also.

“Yes”

What exactly you do to add that functionality in the class?

“Extend the class and add the method and then use my own extended class”

If so, Kotlin extensions can definitely make your life easier.

Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use any type of design pattern such as Decorator. This is done using a special declaration called extensions. These extensions basically add some functionality in an existing class without extending the class.

And how do we use it?

I will explain this but before that, keep in mind that Kotlin supports both, extension functions and extension properties (Without backing field). Let’s understand them one by one.

Extension Functions

To declare an extension function, we need to prefix its name with a receiver type, i.e. the type being extended or the class name.

Let’s say we want to add a function in the List Interface implementation which returns the element at the mid position of the list.

fun <T> List<T>.midElement(): T {
    if (isEmpty())
        throw NoSuchElementException("List is empty.")
    return this[size / 2]
}

//to call this method
var list = listOf<Int>(1, 2, 3, 4, 5)
var mid = list.midElement()

//or 

var arrayList = arrayListOf(5, 4, 3, 2, 1)
var mid = arrayList.midElement()

So what we are doing here?

We are adding a method named midElement in the List implementation which returns us the element at the mid position of the list. This method throws NoSuchElementException if the list is empty.

Later this midElement method can be called with any normal List object.

Let’s take another example of our very favorite Toast class in Android.

Toast class is used to show a message at the bottom half of the screen in Android for a long or short duration.

The native code to show a Toast in Android is

Toast.makeText(this, Hello, Toast.LENGTH_SHORT).show()

The above code calls a static method makeText of Toast class with three parameters (reference of the current Context class, the CharSequence to be displayed and the Int duration for which the toast to be shown). The static method returns the Toast class object to which we call the show method.

Now to make the call easy to this Toast from any class which extends the Context class directly or indirectly, we can create an extension method of Context class which will do the exact same.

fun Context.showToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, text, duration).show()
}

//to call this method from any context extending class
showToast("Hello")

How are extensions resolved?

Statically.

Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.

The extension functions dispatched statically. That means the extension function which will be called is determined by the type of the expression on which the function is invoked, not by the type of the result of evaluating that expression at runtime. In short, they are not virtual by receiver type.

Let me explain.

Let’s say we have two classes, one extending other and both have the same extension functions with same name and signature.

open class BaseClass

class DerivedClass : BaseClass()

fun BaseClass.someMethod(){
    print("BaseClass.someMethod")
}

fun DerivedClass.someMethod(){
    print("DerivedClass.someMethod")
}

Now if we try to call the someMethod with BaseClass object but the actual reference we pass to it will be DerivedClass reference,

fun printMessage(base : BaseClass){
    base.someMethod()
}

//actual call
printMessage(DerivedClass())

This will print BaseClass.someMethod

But why?

I passed the DerivedClass reference.

Because the extension function being called depends only on the declared type of the parameter base in printMessage method, which is the BaseClass class. This is different from runtime polymorphism as here it is resolved statically but not at the runtime.

What if my class has the same method with the same signature that we are adding as an extension function?

The compiler will call the method declared in the class but not the extension function.

class BaseClass{
    fun someMethod(){
        print("I am the actual someMethod")
    }
}

fun BaseClass.someMethod(){
    print("I am the extension function")
}

//to call this method
BaseClass().someMethod()

This will call the someMethod which s defined in BaseClass and will print

I am the actual someMethod

Nullable Receiver

The extensions can be defined with a nullable receiver type also. These extension function can be called on a nullable object variable.

Let’s take an example of toString method.

fun Any?.toString(): String {
    if (this == null) return "null"
    // after the null check,
    // 'this' is autocast to a non-null type, 
    // so the toString() below
    // resolves to the member function of the Any class
    return toString()
}

// not the above method can be call on a nullable variable also
var nullableVariable : Any? = null
nullableVariable.toString()

Extension Properties

Similarly to functions, we can add extension properties also.

val <T> List<T>.midIndex: Int
    get() = if (size == 0) 0 else size / 2

Since extensions do not actually insert members into classes, there’s no efficient way for an extension property to have a backing field. This is why initializers are not allowed for extension properties. Their behavior can only be defined by explicitly providing getters/setters.

class BaseClass{
}

var BaseClass.index : Int = 10 <e>//compile error</e>
//Extension property cannot be initialized
//because it has no backing field

Companion Object Extension

We can also define extension functions and properties for the Companion object.

class BaseClass {

    companion object {
    }
}

fun BaseClass.Companion.companionMethod(){

}
//to call this method
BaseClass.companionMethod()

Scope of Extensions

Most of the time we define extensions on the top level, i.e. directly under packages.

package com.example.extension

fun BaseClass.extensionFunction(){
    
}

//and use it with
import com.example.extension.extensionFunction 
// importing all extensions by name "extensionFunction"

// or
import com.example.extension.*   
// importing everything from "com.example.extension"

fun usage(base: BaseClass) {
    base.extensionFunction()
}

but if you want to declare an extension function as a member of the other class we have some rules to follow which I will explain in another blog.

Read more about extension functions on the below links.

Comments