Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shortened reference to class method when the receiver is known from context

In the following code,

class IncWrapper<T> (val wrapped: T, val base: Int) {
    fun incFunction(increment: Int, func: T.(Int) -> Int): Int {
        return increment + wrapped.func(base)
    }
}

class ClassWithIndecentlyLongName {
    fun square(x: Int) = x * x
}

fun main() {
    val wrapper = IncWrapper(ClassWithIndecentlyLongName(), 2)
    val computed = wrapper.incFunction(1, ClassWithIndecentlyLongName::square)
    println(computed)
}

we pass a reference to a method of the wrapped class ClassWithIndecentlyLongName. Since it is known at the call site that this class is expected as the receiver of the method, it seems awkward / redundant to pass the name of the class again. I'd expect something like ::square to work, but it doesn't. If such feature is missing, what might be the reasons?

(The question arose from an attempt to refactor some very wordy Java code converting lots of fields of one class to another.)

like image 968
Inego Avatar asked Sep 13 '19 07:09

Inego


People also ask

What is the name of the variable that corresponds to the receiver object?

The variable name is known as the receiver of the call because it's the variable that the extension function is acting upon. In this case, the block passed to let() is only evaluated if the variable name was non-null. This means that inside the block, the value n is guaranteed to be non-null. More on this here.

What is context receiver?

With Context Receivers we can add one or more contexts to our functions or methods. This provides the functionality of the declared context within our function, like having another this in that function. We are then able to call methods of that context as if our function is part of the object.

What is a receiver of function in Kotlin?

It means that functions can be assigned to the variables, passed as an argument, or returned from another function. While Kotlin is statically typed, to make it possible, functions need to have a type.

What is with in Kotlin?

Kotlin with. Like apply , with is used to change instance properties without the need to call dot operator over the reference every time. data class Person(var name: String, var tutorial : String) var person = Person("Anupam", "Kotlin") with(person) { name = "No Name" tutorial = "Kotlin tutorials" }


1 Answers

Using just ::square would mean, it is part of your package or the file/class where it is called from. But that's not correct in that case.

If you have such long names you could switch from the function reference to the actual lambda instead, e.g.:

wrapper.incFunction(1) { square(it) }

If you have more parameter, then a typealias is probably more helpful instead, e.g.

typealias Functions = ClassWithIndecentlyLongName // choose a name that's more appropriate

// and calling it as follows:
wrapper.incFunction(1, Functions::square)

Alternatively import that class with an alias, e.g.:

import ClassWithIndecentlyLongName as ShortName

However, the better approach is probably to just discard the indecently long name and replace it with something more appropriate instead.

Finally, if you really want to just use ::square you can still do so, if you supply as many wrapper functions that you need, e.g.:

fun square(c : ClassWithIndecentlyLongName, i : Int) = c.square(i)

// calling it, now works as you wanted:
wrapper.incFunction(1, ::square)

Now: why it is that way? I can only guess. But it makes sense to me, that you need to specify exactly where that function can be found. I think it would rather complicate the code if you can never be sure which function is exactly behind the one specified.

like image 111
Roland Avatar answered Oct 19 '22 02:10

Roland