While building an application, we use a lot of third-party libraries or classes that are written by someone else. Normally, when we want to extend the functionalities of such classes, we would inherit the class and add our additional functions or properties to its subclass. Thanks to Kotlin, we can now easily add new features to any classes that cannot be modified or belong to a third-party library using extensions. Extensions enable us to add functions as well as properties to any class.

Extension Function

Let’s see the various components of an extension function. The class for which extensions are created is called the Receiver type (i.e Calculator class in this example). The rest of the syntax is the same as a normal function in Kotlin.

Fig. 1: Syntax of Extension function

This way we can add a function named add() to a class Calculator.

Properties of an Extension function

Now, we know the use case of an extension function, let’s learn the different properties of it.

  1. Extension function can invoke the member functions as if they are in the same class. 
fun Calculator.add(operand1: Int, operand2: Int) {
    val result = operand1+operand2
    printResult(result)
}
  1. The member function of any class will always have a higher priority compared to the extension function.
class Calculator {
    fun printResult(result: Int) {
        println("From calculation = $result")
    }
}

fun Calculator.printResult(result: Int) {
    println("From extension function = ${result}")
}

fun main(args: Array<String>) {
    Calculator().printResult(23)
}

Output

As you can see in the example, printResult() method is present as the member function as well as an extension function, when calling this method the member function gets executed. So, be careful with the naming conflict while declaring extension function.

  1. Extension functions can have the same name as one of the member functions but with different signatures. This phenomenon is called overloading methods. 
class Calculator {
    fun printResult(result: Int) {
        println("From calculation = $result")
    }
}

fun Calculator.printResult(operand1: Int, operand2: Int) {
    println("From extension function = ${operand1 + operand2}")
}

fun main(args: Array<String>) {
    Calculator().printResult(5, 13)
}

Output

  1. Extension function can also be used with the nullable receiver type. We can access the receiver type class inside the extension function using this keyword.
class Calculator {
    var output: Int = 0
    fun printResult(result: Int) {
        println("From calculation = $result")
        output = result
    }
}

fun Calculator?.outputResult(): String {
    if(this == null) return "null"
    return this.output.toString()
}

fun main(args: Array<String>) {
    val calc = Calculator()
    calc.printResult(23)
    println(calc.outputResult())
}

Output

  1. Often extension function is declared as a top-level function. However, it is also possible to write it inside a class.
class Calculator {
    fun printResult(result: Int) {
        println("From calculation = $result")
    }
}

class Computation {
    fun printResult(result: Int) {
        println("Computation = $result")
    }

    fun Calculator.performOperation(operand1: Int, operand2: Int, operator: String) {
        val result = when (operator) {
            "+" -> operand1 + operand2
            "-" -> operand1 - operand2
            else -> operand1 * operand2
        }
        printResult(result)
    }

    fun calculator() {
        Calculator().performOperation(2, 3, "+")
    }
}

fun main(args: Array<String>) {
    val computation = Computation()
    computation.calculator()
}

performOperation is an extension function with receiver type Calculator inside class Computation. This method invokes the function printResult  which is present in both the classes Computation and Calculator. But since it is invoked from the extension function, the member function of the receiver types i.e Calculator gets the priority.

Extension Properties

Similar to extension functions we can also declare extension properties. Let’s see an instance of it.

Fig. 7: Syntax of extension properties

Note that we cannot directly assign a concrete value to the extension function. 

Fig. 8: Error while initializing the property

We can only define the behavior of an extension property using getter and setter as we see in figure 7.

To summarize, extensions are extremely useful because it makes it easier to work with third-party library classes or unmodifiable classes. Kotlin provides a huge amount of built-in extension functions to the libraries of Java class which contributes to the interoperability of Kotlin with Java. Click on the next button to continue learning.

Category
Tags

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *