Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between function receiver and extension function

Tags:

kotlin

I was reading about Kotlin and did not quite get the idea

from What I understood extension function gives ability to a class with new functionality without having to inherit from the class

and what is receiver the same except it can be assigned to variable

Is there anything else about it?

Can someone give some examples on it

like image 550
roki Avatar asked Sep 01 '17 11:09

roki


2 Answers

Extension functions:

Like Swift and C#, Kotlin provides the ability to extend a class with new functionality without having to modify the class or inherit from the class.

You might wonder why? Because we cannot edit and add functions to the language or SDK classes. So we end up creating Util classes in Java. I believe all the projects have a bunch of *Utils classes to put the helper methods that are used at multiple places in the code base. Extensions functions help to fix this Util problem.

How do we write a helper method in Java to find whether the given long value refers to today?

public class DateUtils {

    public static boolean isToday(long when) {
        // logic ...
    }
}

And we call that method by passing the long value as an argument:

void someFunc(long when) {
    boolean isToday = DateUtils.isToday(when);
}

In Kotlin, we can extend the Long class to include the isToday() function in it. And we can call the isToday() function on the Long value itself like any other member functions in the class.

// Extension function
fun Long.isToday(): Boolean {
    // logic ... 
}

fun someFunc(time: Long) {
    val isToday = time.isToday()
}

Compared to the Util methods, Kotlin provides a much richer syntax using the Extension functions.

This improves the readability of the code which in turns improves its maintainability. And we get a little help from the code completion of the IDE. So we don't have to remember which Util class to use for the desired function.

Under the hood, Kotlin compiler generates the static helper methods as though we had written them as Java static Util methods. So we get this nice and richer syntax in Kotlin without sacrificing any performance.

Similar to functions, Kotlin also supports extension properties where we can add a property to an existing class.

Higher order functions:

A higher-order function is a function that takes functions as parameters, or returns a function.

Lets look at how a higher order function is written.

fun execute(x: Int, y: Int, op: (Int, Int) -> Int): Int {
    return op(x, y)
}

Here the third parameter ( op ) is a function and so it makes this function a higher order function. The type of the parameter op is a function that takes 2 Ints as parameter and returns a Int.

To invoke this Higher order function, we can pass a function or a lambda expression:

execute(5, 5) { a, b -> a + b }

Receiver (or Function literal with Receiver or Lambda with Recevier):

A Higher order function that takes an extension function as its parameter is called Lambda with Receiver.

Let's look at the implementation of the apply function which is available in the Kotlin standard library.

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

The function we pass to this apply function is actually an extension function to the type T. So in the lambda function, we can access the properties and the functions of the type T as though we are writing this function inside class T itself.

Here the generic type T is the receiver and we are passing a lambda function, hence the name Lambda with Receiver.

Another Example:

inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) {
    beginTransaction()
    try {
        func()
        setTransactionSuccessful()
    } finally {
        endTransaction()
    }
}

Here, the inTransaction() is an Extension function to the SQLiteDatabase class and the parameter of the inTransaction() function is also an extension function to the SQLiteDatabase class. Here SQLiteDatabase is the receiver, for the lambda that is passed as the argument.

To invoke that function:

db.inTransaction {
    delete( ... )
}

Here the delete() is the function of the SQLiteDatabase class and since the lambda we pass is an Extension function to the receiver SQLiteDatabase we can access the delete function without any additional qualifiers with it, as though we are calling the function from inside the SQLiteDatabase class itself.

like image 144
Bob Avatar answered Nov 03 '22 18:11

Bob


While @Bob's answer is far more informative on Kotlin than could I hope to be, including extension functions, it doesn't directly refer to the comparison between "function literals with receiver" as described in https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver and extension functions (https://kotlinlang.org/docs/reference/extensions.html). I.e. the difference between:

val isEven: Int.() -> Boolean = { this % 2 == 0 }

and

fun Int.isEven(): Boolean = this % 2 == 0

The receiver part of the name refers to both of these syntaxes receiving the base Int argument as this.

As I understand it, the difference between the two is simply between one being an expression confirming to a function type and the other a declaration. Functionally they are equivalent and can both be called as:

when { myNumber.isEven() -> doSomething(myNumber) }

but one is intended for use in extension libraries, while the other is typically intended for use as an argument for a function with a function-type parameter, particularly the Kotlin builder DSLs.

like image 41
Typhlosaurus Avatar answered Nov 03 '22 18:11

Typhlosaurus