DEV Community

Sagar
Sagar

Posted on • Updated on

Kotlin: Extension function and Receiver type

Prerequisite/Previous Part

Kotlin: Function type, function literal, lambda expression and anonymous function

Extension Function

General scenario: We make a class. We write a few methods. Later, we realize that we need more methods for the class. We write more methods in the same class.

But, if the class is in the third-party library that we are using through dependency or in such a way that we cannot add new functions in such a class, we cannot modify that class.

In kotlin, we can add a new function for any class in any other class too. Such a function is called an “Extension function” and we can use such an extension function only inside the class where it is created.

So now, we can add new functions even for the class that is in the third-party library. Note that it is not limited to third-party library class only! Main thing is, we can add a new function for any class in any other class too.

Syntax

In order to create an extension function, we write the class name (also known as a receiver type) followed by a dot, followed by our function as below:

//Here, OurClassName is called a receiver type
fun OurClassName.ourExtensionFunction

For example:

/**
 * Extension function. Here, String is known as receiver type. 
 * That means, we can call this function on any String. 
 * The extension function can be called only from the   
 * class where it is defined.
 */
fun String.extensionFun() {
//We can access the receiver (Here, it is String) using "this" keyword inside the //business logic
     println("from extension function: $this")
}

We can access an instance of the receiver type using this keyword inside the business logic as shown above.

How to call / use an extension function?

val myString = "MyString"
myString.extensionFunction() //prints: from extension function: MyString

Receiver

We have seen a receiver type in an extension function. Similarly, a function type, function literal, lambda expression and an anonymous function can also have a receiver type.

A function type with receiver

Syntax/Signature of a function type with receiver:
ft: Int.(Int) -> Int // Here, the receiver type is: Int

Function type with the receiver as a parameter

We can have a higher order function having a function type parameter with receiver as below:

//A higher order function whose function type parameter has a receiver
fun doSomething(x: Int, y: Int, ft: Int.(Int) -> Int) {
   val ftWithReceiver = x.ft(y) //OR ft(x, y) where x is a receiver.
   println("doSomething: $ftWithReceiver")
}

Note that when a receiver is treated as an argument, it must be a first argument as shown in the comment of the above function.

In order to call above higher order function, we can do one of the below things:

Function literal with receiver

//A function literal, accessing the receiver by using the keyword: this
doSomething(1, 2) { y: Int -> this - y } //prints: doSomething: -1

Let us understand our “function literal”.

{ y: Int -> this - y }
  • Our function literal has been passed as an argument against:

ft: Int.(Int) -> Int)

  • The function type parameter is expecting an Int parameter. Our function literal has also an Int parameter:

    y: Int

  • Inside the business logic, we have used the keyword: this

    -> this - y

  • We access an instance of our receiver in a business logic of an extension function through the keyword: this

  • Our this keyword has been inferred as a receiver type: Int for:

ft: Int.(Int) -> Int)

  • Our this keyword refers to a receiver type: Int

Lambda expression with receiver

//A lambda with receiver type. Accessing the receiver by using the keyword: this
val ftSubtraction: Int.(Int) -> Int = { y: Int -> this - y }

And then we can use that lambda to pass as an argument to our higher order function as below:

//passing a lambda for the higher order function
doSomething(1, 2, ftSubtraction) //prints: doSomething: -1

Same receiver for both an extension function and a function type

Suppose we have a higher order function like below:

//Same receiver type for both an extension function and its function type param
fun Int.doSomething(y: Int, ft: Int.(Int) -> Int) {
    //In such case, we can omit the receiver while calling the function type
    val ftWithReceiver = ft(y) //It is Int.ft(y) 
    println("doSomething: $ftWithReceiver")
}

So, as shown in the example above: When the receiver type of an extension function is the same as its function type parameter, we can omit the keyword this while calling any function on it.

We can call above extension function like below:

Using Anonymous function
//passing an anonymous function
1.doSomething(2) { y: Int -> this - y } //prints: doSomething: -1
Using lambda
//passing a lambda for the higher order function
1.doSomething(2, ftSubtraction) //prints: doSomething: -1

You can play the concept below:

That's all!

If you have read this article, if this article has helped you, you can give me your ❤ 😉

Link to previous article

Next article: inline

Let us be Connected

https://www.linkedin.com/in/srdpatel

https://twitter.com/iSrdPatel

Tags: kotlin, function type, function literal, lambda expression, anonymous function, higher order function, extension function, receiver type

Top comments (0)