Differentiate the Extension Functions in Kotlin

  1. Create an Extension Function in Kotlin
  2. Use Lambda Functions With Extension Functions in Kotlin
  3. Use the let() Extension Function in Kotlin
  4. Use the also() Extension Function in Kotlin
  5. Use the apply() Extension Function in Kotlin
  6. Use the takeIf() Extension Function in Kotlin
  7. Use the takeUnless() Extension Function in Kotlin
  8. Conclusion

In object-oriented programming, we learned the concepts of inheritance, encapsulation, abstraction, and polymorphism, but these are not enough to structure our application in a flexible, reusable, and maintainable way.

Design patterns, with the help of design principles and OOP concepts, came to the rescue to introduce solutions to common design problems. One design pattern very similar to what we will cover in this tutorial is the decorator design pattern.

The decorator design pattern allows us to add functionality to our application without extending from another class. In Kotlin, we can achieve the same functionality design patterns do by using Kotlin extension functions.

Extension functions in Kotlin add functionality to a class without extending from another class or using the decorator design pattern. Behind the scenes, extension functions can be generic and can use lambda functions to realize the functionality we have mentioned.

In this tutorial, we will learn dive deep into each of the extension functions and give an example for each. The extension functions covered in this article include: apply(), also(), let(), takeIf(), and takeUnless().

Create an Extension Function in Kotlin

To create an extension function, define the type that you want the functionality added to, such as String, Number, or a class, and use the dot operator to define the method delegated the functionality. Define the return type of the extension function and finally implement the method’s logic on the same line.

Go to Intellij code editor and create a new project named extension-exercise. Under the src, create the folder structure com.extension.

Create a Main.kt file under the extension folder and paste the following code.

package com.extension

fun Number.isGreaterThanTen(): Boolean = this.toInt() > 10;

fun main() {
    val  num = 100;
    println(num.isGreaterThanTen());
}

In the above code, we have defined an extension function named isGreaterThanTen() on type Number that checks whether a number is greater than 10 and returns a Boolean value.

If you invoke any variable of type number from this point onwards, you will have access to the isGreaterThanTen() method, as shown in the main method above.

Note that a Boolean value of true is logged to the console when you run the above program.

true

Use Lambda Functions With Extension Functions in Kotlin

Behind the scenes, a lambda function can be defined as an interface that contains only one method. The lambda function is passed anonymously to a concrete function without specifying the function name, and the return type is then defined using the arrow symbol.

Create another file and move the previous code to the file. Copy and paste the following example to the empty Main.kt file.

package com.extension

fun Number.isGreaterThanTen(block: (Number) -> Boolean): Boolean = block(this);

fun main() {
    val num = 100;
    println(num.isGreaterThanTen { number ->
        number.toInt() > 10
    })

}

In the above code, we have defined an extension function named isGreaterThanTen() that accepts a lambda function as a parameter and returns a Boolean.

The lambda function, which we have named block, accepts a parameter of type Number and returns a Boolean value. This Boolean value returned by the lambda function is delegated to the extension function.

Compared to the previous example, we define the logic of the extension function after invoking the isGreaterThanTen() on any variable of type Number inside the main function.

Run the above code and observe that we have the same result as the previous example. The output is as shown below.

true

Use the let() Extension Function in Kotlin

Move the previous code to the new file we created and paste the following code into the Main.kt file.

package com.extension

inline fun <T, R> T.let(block: (T) -> R): R = block(this);

fun  main(){
    val num = 10;
    val result = num.let { number -> number.toString() }
    println(result);
    println(result::class.java.typeName);
}

The let() function is defined using generics, meaning it can use any type. The object invoking the let() function is passed as the parameter of the lambda function.

The lambda function is defined inside the let() function and returns a different object from the one passed as the parameter.

Since the value is returned by the let() function is the same as the function returned by the lambda function, we have delegated the lambda function return value to the let() function.

In the main method, we have defined a variable of type Number and invoked our extension function to return a string representation of the number.

We have then logged the value returned and its type to the console to verify that the extension function is working as required. Note that the type passed to the extension function was a Number, but the value returned was a String, as shown below.

10
java.lang.String

Use the also() Extension Function in Kotlin

Move the previous code to the new file we created and paste the following code into the empty Main.kt file.

package com.extension

inline fun <T> T.also(block: (T) -> Unit): T {block(this); return  this}

fun main(){
    val  num = 10;
    num.also {i ->
        println(i == 10)
    }
}

The object invoking the also() extension function is passed as the parameter of the lambda function. The lambda function is defined as the argument of the extension function and does not return any value usually denoted by a Unit.

The also() function returns the current variable you are using, which is delegated to the lambda function for further computation. In the main function, we have defined a variable of type Number delegated to the lambda function to check whether the value equals 10.

Run the code and note it returns a true value, as shown below.

true

Use the apply() Extension Function in Kotlin

Move the previous code into the new file we created and paste the following code into the empty Main.kt file.

package com.extension

inline fun <T> T.apply(block: T.() -> Unit): T {block(); return this}

fun main(){
    val num = 10;
    num.apply {
        println(toDouble());
    }
}

The object invoking the apply() extension function is passed as a method invocation of this object to the parameter of the lambda function. The lambda function has no return value.

The definition means we don’t have to use an object of a type to invoke a method because the lambda function handles the functionality. In that case, we only need to call the method we need from a type.

In the main function, we have defined a variable of type Number, and using the apply() method, we have converted the Number to a Double by calling the toDouble() method. Note that no reference to an object was used to call the toDouble() method.

Run the above code and note that the value logged to the console is Double, as shown below.

10.0

Use the takeIf() Extension Function in Kotlin

Move the previous code to the new file we created and paste the following code into the empty Main.kt file.

package com.extension

inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null

fun main() {
    val  num = 10;
    println(num.takeIf { i ->
        i.plus(10) < 30
    })
}

The object invoking the takeIf() extension function is passed as the parameter of the lambda function. The lambda function is passed as the argument of this method and returns a Boolean value.

The extension function returns the invoking object only when the condition of the lambda expression evaluates to true. Otherwise, it returns a null value.

In the main function, we have defined a value of type Number that uses the takeIf() to determine if this number plus 10 is less than thirty. If the condition evaluates to true, the value 10 is returned; otherwise, a null value is returned.

Run the above code and note that the lambda expression evaluates to true, and the value 10 is returned, as shown below.

10

Use the takeUnless() Extension Function in Kotlin

Move the previous code to the new file we created and copy and paste the following code into the empty Main.kt file.

package com.extension

inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null

fun main() {
    val num = 10;
    println(num.takeUnless { i ->
        i.plus(10) < 30
    })
}

The object invoking the takeUnless() extension function is passed as the parameter of the lambda function. The lambda function is defined in this method and returns a Boolean value.

The difference between takeIf() and takeUnless() is that this example only returns a value when the lambda expression evaluates to false. This means that the value returned by the extension function is the inverse of the Boolean function returned by the lambda function.

Inside the main method, we have defined the same example as above, but note that this example will return null because the lambda function evaluates to true, which gets inverted to false.

null

Conclusion

This tutorial taught us how to add functionality in Kotlin using the extension functions. The extension functions covered include: using let(), also(), apply(), takeIf(), and takeUnless().

Note that these extension functions are already defined in the Kotlin API, and you do not have to define them as we have done in this tutorial.

Write for us
DelftStack articles are written by software geeks like you. If you also would like to contribute to DelftStack by writing paid articles, you can check the write for us page.