Is it possible to null T or 'this' at the end of generic function for Kotlin? - kotlin

I have in my project a listener. It is assigned to drawerLayout. I would like to in lambda function remove it and null it at once (sequentially). Is it possible to null T or this at the end of generic function.
Here is my code:
// Usage
actionBarListener?.let {
drawerLayout.removeDrawerListener(it) // remove listener
actionBarListener = null // null it
}
// Usage expected
actionBarListener.releaseAndSetNull {
drawerLayout.removeDrawerListener(it) // remove listener and null it
}
// Generic
fun <T> T?.releaseAndSetNull(block: (T?) -> Unit) = apply {
this?.apply { block.invoke(this) }
this = null // Error: variable expected
}

As Ivo Beckers said, this function would only work on vars, i.e. KMutableProperty0<T>. So you could write an extension on KMutableProperty0<T?>, and use reflection to set it, if you don't mind using reflection, that is.
inline fun <T: Any> KMutableProperty0<T?>.releaseAndSetNull(block: (T?) -> Unit) {
block(this.get())
this.set(null)
}
// or if you don't want the block to be called if the property is null:
inline fun <T: Any> KMutableProperty0<T?>.releaseAndSetNull(block: (T) -> Unit) {
this.get()?.run(block)
this.set(null)
}
Then suppose you have a property:
var foo: Int? = 10
You can do:
::foo.releaseAndSetNull { println("Foo: $it") }
// or if foo belongs to someObject
someObject::foo.releaseAndSetNull { println("Foo: $it") }
Looking at the generated bytecode, the way this is implemented (which is subject to change) is that each unique property referred to by a property reference in this way causes an inner class to be generated. The inner class will then have get and set methods that do their jobs with little extra cost - as they can just set the right property directly. So really the main cost is the extra inner class that is generated.

I can think of several reasons why this could never work.
First of, the generic function doesn't know if this is a var or val. And this functionality could only works on a var
Likewise, it can't know if it's nullable, that's also a requirment.
Furthermore, it can even be the case that it's not a variable that's calling the function.
Like say you have
fun getActionBarListener() {
return actionBarListener
}
Then somewhere else you could do
getActionBarListener().releaseAndSetNull {
drawerLayout.removeDrawerListener(it) // remove listener and null it
}
How do you expect that to work?
Or even anonymous objects could call this function.

Related

How to call constructor default lambda using Kotlin Refelction?

Trying to call lambda provided by MyClass constructor using Kotlin Reflection.
data class MyClass(
var magic:Int=2,
var lambdaValue: ()->String = { //trying to call this lambda from reflection
"Working"
},
)
fun main(args: Array<String>) {
val clazz=MyClass::class
val obj=clazz.createInstance()
val kProperty=clazz.memberProperties
clazz.constructors.forEach{cons-> // for each construtor
cons.parameters.forEach{ parameter-> // looping through constructor parameters
val property=kProperty.find { it.name==parameter.name } // finding the exact property
print(parameter.name+" : ")
if(parameter.type.arguments.isEmpty()) // if empty Int,Float
{
println(property?.get(obj))
}else{
println(property?.call(obj)) // unable to call lambda
}
}
}
}
property.call(obj) returns Any which is not invokable. Any solution?
Expected:
magic : 2
lambdaValue : Working
Frankly speaking, I'm not sure what was your idea behind parameter.type.arguments.isEmpty(). It seems unrelated to what you try to do.
If we have a value of the property already, we can simply check its type and if its is a function then invoke it:
val value = kProperty.find { it.name==parameter.name }!!.get(obj)
print(parameter.name+" : ")
when (value) {
is Function0<*> -> println(value())
else -> println(value)
}
I think usefulness of such a code in generic case isn't very high. This code doesn't know what is the function and if it is going to return a value or perform some action, etc. Maybe in your specific case it is more useful.

Kotlin context receivers cannot resolve members of generic type

In the following code, the call member of Animal cannot be resolved even though Cat is specified as context receiver and it has a member named call.
interface Animal { val call: String }
object Cat : Animal { override val call: String = "Meow" }
object Dog : Animal { override val call: String = "Woof" }
fun <T : Animal> acquireAnimal(animal: T, block: context(T) () -> Unit) {
block(animal)
}
fun main() {
acquireAnimal(Cat) {
call
}
}
When I type this inside the lambda, then the IDE seems to suggest that the type of this is Any?.
If I do the same with a function without a generic context receiver, then it seems to get the type right.
Is this a limitation that is by design or is this a bug?
The fact that you cannot access call was a bug, which was fixed in Kotlin 1.7.20.
A workaround for lower versions is:
sealed interface TypeWrapper<out A> {
object IMPL: TypeWrapper<Nothing>
}
fun <T: Animal> acquireAnimal(animal: T, block: context(T) (TypeWrapper<T>) -> Unit) {
block(animal, TypeWrapper.IMPL)
}
fun main() {
acquireAnimal(Cat) {
val x = call // works!
}
}
However, the fact that this doesn't work is intended. Context receivers do not change the meaning of this. Since you are in a global function, this does not mean anything, and the existence of a context receiver does not change that.
Normally, to access the context receiver itself, you need to do a qualified this by appending the generated label for the context receiver:
context(Foo)
fun foo() {
val x = this#Foo
}
However, your context receiver is a type parameter, so according to the rules here, I don't think a label is generated for the context receiver.

Kotlin - Automatically match overriden function type?

I'm trying to write a function that is essentially a wrapper method around some other functionality, for instance, some logging function.
I've tried several combinations of inline, generic, reified, etc., but nothing seems to work.
My function looks like this:
fun log(note: String, block: () -> Unit): () -> Unit {
print(note)
return block
}
My idea here is to perform some simple operation on the incoming note, and then just return that incoming function to be used as it was originally.
However, I want to do this around overridden functions like so:
override fun onClick(clicked: View) = log("Green Button") {
// here the regular onClick functionality goes
}
Here, I get an error "Return type is () -> Unit, which is not a subtype of overridden". This makes sense enough, as the function signatures do not match.
However, when I do this with other random functions:
fun test() = log("foo") { ... }
fun otherTest(a: String, b: Int) = log("bar") { ... }
I get no errors, and the interpreter somehow seems fine with this. I also tried looking at something like GlobalScope.launch to take that approach, but I couldn't figure it out.
Is what I'm trying to do possible? If not, is there something close?
I think
inline fun log(note: String, block: () -> Unit): Unit {
print(note)
return block()
}
should do what you want. It can be generalized to
inline fun <T> log(note: String, block: () -> T): T {
print(note)
return block()
}
I get no errors, and the interpreter somehow seems fine with this.
Why is that surprising? Those functions just return () -> Unit. If you do e.g.
fun test() = log("foo") { print("bar") }
then calling test() won't print bar; calling test()() will.
Tell me if my understanding is wrong. This is my approach
Extension function:
fun View.onClickWithLog(str: String, l: () -> Unit) {
setOnClickListener { Log.d("LogTag", str); run(l) }
}
Usage (from Activity):
btnTest.onClickWithLog("My Button String"){
Log.d("Actions from Activity", "Content")
finish()
}
and the output is
D/LogTag: My Button String
D/Actions from Activity: Content
which prints your note first, and execute the actions in the lambda expression.
When you use the = operator to assign something to a fun, the expression on the right hand side is supposed to return the return type of that fun
The original fun onClick(clicked:View) : Unit has return type Unit. When you write
override fun onClick(clicked:View) = ... , the ... is what you get when you call onClick(v) so it should be a Unit instead of a View -> Unit (Not even () -> Unit as in your code)
Take a simpler example. Let say you have fun sum(a:Int,b:Int) : Int. When you write override fun sum(a:Int,b:Int) = ... , ... must be an Int instead of a (Int,Int) -> Int since you expect to get an Int immediately when you call sum(a,b). If you somehow got a let say
val someOtherWayToSum : (Int,Int) -> Int = {...}
and want to use it, you can write
override fun sum(a:Int,b:Int) = someOtherWayToSum(a,b)
In your case, you better just do
override fun onClick(clicked:View){
/* some operation (e.g your log)*/
/* the regular onClick functionality */
}
since you are overriding it and implementing its regular functionality right there anyway.

Kotlin: store any type of function reference in a variable

Is it possible to have a variable that can holds any type function.
Like :
fun method1(par: Boolean){}
fun method2(par: Boolean) : Int{return 1}
fun method3(par: Boolean, par2: Boolean) : Int{return 1}
var funtionHolder : ((Any)->Any) ?= null //What should I write here?? so to hold any type of function
fun method4(){
.........
funtionHolder = ::method1 //getting compile time error
.........
funtionHolder = ::method2 //getting compile time error
.........
funtionHolder = ::method3 //getting compile time error
}
After holding the function_reference I need to invoke it later. So I need to holds it parameter type and state also.
You can hold them in a KFunction<Any> or its superclass KCallable<Any> because you know nothing about the parameter list and nothing about the return type, so you have to go to something that can reference at that level of abstraction. These instances can then be invoked more generically using the call() or callBy() methods. (this requires the kotlin-reflect dependency). To do something safer and to call like a normal function you'd have to cast back to the specific function type later.
If you want to avoid this, you'll need to unify your signatures to something you can point to with another function type (i.e. KFunction1 or KFunction2). Otherwise how you'll call this, what you'll do with it will be up to you at this point because you erased all the information that allows you to easily call the function.
val functionHolder1: KFunction<Any> = ::method1 // success!
val functionHolder2: KFunction<Any> = ::method2 // success!
val functionHolder3: KFunction<Any> = ::method3 // success!
You can then make a DeferredFunction class to hold these along with parameters you want to later pass, and then invoke it whenever in the future.
class DeferredFunction(val function: KFunction<Any>, vararg val params: Any?) {
#Suppress("UNCHECKED_CAST")
operator fun <T> invoke(): T {
return function.call(params) as T
}
}
fun whatever(name: String, age: Int): String {
return "$name of age $age"
}
val functionHolder = DeferredFunction(::whatever, "Fred", 65)
println(functionHolder<String>()) // "Fred of age 65"
You do not need the generic return type on the invoke function and could just make it return Any or call it as functionHolder<Any>() but it is nice if you know what to expect for the return. You can decide what to do there based on your actual use case. Also no need to special case for no parameters, just don't pass any, i.e. DeferredFunction(::otherFunc)
With reference from Jayson's answer, added extra code to hold the state of the function by using vararg and spread operator(*).
var functionHolder: KFunction<Any> ?= null
var paramsHolder : Array<out Any?> ?= null
fun hold(functionReference : KFunction<Any>, vararg args : Any?) {
this.functionHolder = functionReference
this.paramsHolder = args
}
fun release() {
if (functionHolder != null) {
if (paramsHolder != null) {
functionHolder?.call(*paramsHolder!!)
} else {
functionHolder?.call()
}
}
}
......
fun method3(par: Boolean, par2: Boolean) : Int{return 1}
......
hold(::method3, true, false)
release()//it works
No. Kotlin is static typed language and doesn't allow this. Else what happens when this is called?
functionHolder->invoke(3)
and when functionHolder is assigned a lamda that doesn't take parameter?

Why is subtype function overloading not possible with Kotlin sealed classes?

Let's say I have sealed class I'm using for a server response:
sealed class Response{
class Success: Response()
class ErrorA: Response()
class ErrorB: Response()
}
And a bogus response:
fun getResponse(): Response{
val r = Random()
return when (r.nextInt(3)) {
0 -> { Response.Success() }
1 -> { Response.ErrorA() }
2 -> { Response.ErrorB() }
else -> { throw IllegalStateException() }
}
}
And I want to handle the response. I currently could use something like this:
fun handle(response: Response) = when (response) {
is Response.Success -> { handle(response) }
is Response.ErrorA -> { handle(response) }
is Response.ErrorB -> { handle(response) }
}
Which the compiler will then ensure handles all cases. An awesome feature!
Why, though, could I not do something like this:
class ResponseHandler(){
fun handle(success: Response.Success) {}
fun handle(error: Response.ErrorB) {}
fun handle(error: Response.ErrorA) {}
}
and call
ResponseHandler().handle(response)
This achieves the same thing but does not compile, my question is this: in the same way that the compiler ensures, at runtime, that all cases are handled in a when statement, why can the same logic not be applied to method overloading?
Any information or referrals to further reading would be hugely helpful. Thanks
In principle it could be done (essentially by auto-generating the handle(response: Response) = when ... method). But I don't think it's ever likely to be. Overloading in Kotlin works basically the same as in Java/Scala/other JVM languages and introducing a major difference for so little benefit doesn't looks like a good idea (of course this doesn't apply to when which is Kotlin-specific).
If you want it, you can just define the same fun handle(response: Response) inside ResponseHandler (and make the other handle methods open so it's actually useful).
This problem can be broke down to this simplified example:
fun calc(i: Int) = i * 2
fun calc(d: Double) = d * 2
fun main(args: Array<String>) {
val i: Number = 5
calc(i)
}
You have two specialized methods that take an Int and Double respectively. Your value is of type Number (supertype of both, Int and Double). Although i obviously is an integer, your variable has a type Number, which cannot be an argument to either calc(i: Int) or calc(d: Double).
In your case, you get a Response and want to invoke one of the overloaded methods, none of which takes a Response directly.