How to access scope function's parameter name with reflection - kotlin

I have a scope function on the Reflect class I created; this class will try to use reflection and set the status property value to the InProgress instance. My challenge here is accessing the name of status from the reflection side so I can set the private field by name.
Reflect(myExampleInstance) {
on { status }.then(InProgress)
}
^ Here, I have status property that I have passed to the on function.
This is the reflection side:
inline fun <reified R> on(
p: T.() -> R,
): OnGoingReflect {
val declaredMember: KProperty1<out T, *> = instance::class.declaredMemberProperties.first {
it.returnType.jvmErasure.java == R::class.java &&
it.getter.call(instance) == p(instance)
}
return OnGoingReflect(instance, declaredMember.name)
}
I try to determine the name of R and set it by using the reflection. I solved the first problem by finding declared member, which works until I have two or more properties with the same type.
For example;
If I have status2 with the same type, my method will return me status on the line of declaredMember because of the first call.
Reflect(myExampleInstance) {
on { status2 }.then(InProgress)
}
The challenge is keeping the syntax of on { propertyName }. If I use KProperty<T> and on { MyClass::status} that solves it, but I am looking for a solution that does not make me change the syntax.
Any suggestions?

Related

Access Implementation's property on variable of type Interface

I'm trying to access the delegate of the property (id) of a class (FooImpl). The problem is, this class implements an interface (Foo), and the property in question overrides a property of this interface. The delegate only exists in the class (not that it could exist in the interface).
The problem is that using the :: operator on a variable of type Foo always returns the property of Foo, not that of the actual instance. The problem in code:
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
interface Foo {
val id: Int
}
class FooImpl(
id: Int,
) : Foo {
override val id: Int by lazy { id }
}
val <T> KProperty<T>.hasDelegate: Boolean
get() = apply { isAccessible = true }.let { (it as KProperty0<T>).getDelegate() != null }
fun main() {
val foo: Foo = FooImpl(1)
println("foo::id.hasDelegate = ${foo::id.hasDelegate}")
println("(foo as FooImpl)::id.hasDelegate = ${(foo as FooImpl)::id.hasDelegate}")
}
This prints:
foo::id.hasDelegate = false
(foo as FooImpl)::id.hasDelegate = true
But this requires compile-time knowledge of the correct implementation. What I'm looking for is accessing the correct propert without having to specify FooImpl there.
The information is present at runtime because the least (!) intrusive workaround I have found so far is adding fun idProp(): KProperty0<*> to Foo and override fun idProp() = ::id to FooImpl and accessing the property using that.
Is there any better way than that?
I came up with this, but I don't know if there's a better way. The problem to work around is that getDelegate() has to return an actual instance of the delegate, so you need an instance of the class to be able to retrieve a delegate instance. It would really be nice if there was a hasDelegate property built in. Your version of hasDelegate will crash from the cast on unbound KProperty1's, which is all we have to work with when the specific class is unknown.
So to retrieve the delegate instance, we need to do search the class instance's member properties by name, which gives us a KProperty with covariant class type of the super-class type. Since it's covariant, we can call a consuming function like getDelegate() without casting to the invariant type. I think this logically should be safe, since we are passing an instance that we know has the matching type for the ::class that we retrieved the property with.
#Suppress("UNCHECKED_CAST")
fun <T: Any> KProperty1<T, *>.isDelegated(instance: T): Boolean =
(instance::class.memberProperties.first { it.name == name } as KProperty1<T, *>).run {
isAccessible = true
getDelegate(instance) != null
}
fun main() {
val foo: Foo = Foo2()
println("foo::id.hasDelegate = ${Foo::id.isDelegated(foo)}")
}
The problem here is that the owner of the property is resolved on compile time, not on runtime. When you do foo::id then foo (so FooImpl) become its bound receiver, but owner is still resolved to Foo. To fix this we wound need to "cast" property to another owner. Unfortunately, I didn't find a straightforward way to do this.
One solution I found is to use foo::class instead of foo::id as it resolves KClass on runtime, not on compile time. Then I came up with almost exactly the same code as #Tenfour04.
But if you don't mind using Kotlin internals that are public and not protected with any annotation, you can use much cleaner solution:
val KProperty0<*>.hasDelegate: Boolean
get() = apply { isAccessible = true }.getDelegate() != null
fun KProperty0<*>.castToRuntimeType(): KProperty0<*> {
require(this is PropertyReference0)
return PropertyReference0Impl(boundReceiver, boundReceiver::class.java, name, signature, 0)
}
fun main() {
val foo: Foo = FooImpl(1)
println(foo::id.castToRuntimeType().hasDelegate) // true
}
We basically create a new instance of KProperty, copying all its data, but changing the owner to the same type as its bound receiver. As a result, we "cast" it to the runtime type. This is much simpler and it is also cleaner because we separated property casting and checking for a delegate.
Unfortunately, I think Kotlin reflection API is still missing a lot of features. There should be hasDelegate() function, so we don't have to provide receivers, which is not really needed to check if property is delegated. It should be possible to cast KProperty to another type. It should be possible to create bound properties with some API call. But first of all, it should be possible to do something like: Foo::id(foo), so create KProperty of the runtime type of foo. And so on.

Kotlin "is" operator for objects in when expression

Lets imagine we have sealed class with data classes and objects inside:
sealed class CarEvent {
data class FilledFuel(val fuelAmount: Float) : CarEvent()
data class ChangedPart(val part: CarPart) : CarEvent()
object StartedEngine : CarEvent()
}
Then we have when statement reducing events:
when(event) {
is CarEvent.FilledFuel -> { do something }
is CarEvent.ChangedPart -> { do something }
CarEvent.StartedEngine -> { do something }
}
The questing is: what is the most true approach for StartedEngine object:
compare it using is operator, or without is operator (using default == inside when statement).
Is there any difference between these two?
Is there any difference between these two?
Yes, is checks if something is an instance of a specific type, where as == will check if one object is the same as another
As an example:
val foo = 5
val result = foo is Int
This will always be true, because foo is a type of integer.
but if you consider
val foo = 5
val result = foo == 7
This will return false, correctly, because these two values aren't the same thing.
In your case, I don't think it would matter, but is would probably make more sense
When has a syntatic sugar of evaluating what's inside parentheses versus each case
When you put nothing, it checks for equality and in other cases it checks what you put there
In case of objects, kotlin creates a class for them and creates a singleton INSTANCE and if you try to look at it from a Java point of view, when you put is StartedEngine it's like checking event instanceof StartedEngine and when put StratedEngine it's like event.equals(StartedEngine.INSTANCE)
so one of them check for type while the other checks for equality
On the other hand, object are strictly singletons which you cannot inherit, or override anything from them so it doesn't matter if you check for equality or is unless you've override equals in that object.
So they're not different in usage, nor in action. They are just different approaches.
sealed class A{
abstract val name:String
object AAA : A(){
override val name get()= "AA"
override fun equals(o: Any?) : Boolean {
println("AAA equality call")
return super.equals(o)
}
}
object BBB : A() {
override val name get()="BB"
}
}
fun main() {
val o : A = A.AAA
when(o){
is A.AAA -> println("o is AAA")
}
when(o){
A.AAA -> println("o==AAA")
}
}
But notice that if the override fun equals in AAA returned false, o==AAA wouldn't get printed also note that in second when, equals gets called and it prints AAA equality call
Output
o is AAA
AAA equality call
o==AAA

How can I get property reference from an class reference or generic type?

I'm trying to replace assign() method with a niceAssign():
class Builder<T : Any>(val kClass: KClass<T>) {
fun <K> assign(prop: KProperty1<T, K>, value: K): Builder<T> = TODO("doing other stuff here")
fun <K> niceAssign(call: KClass<T>.() -> Pair<KProperty1<T, K>, K>) : Builder<T> {
val (prop, value) = call(kClass)
return assign(prop, value)
}
}
val builder = Builder(Data::class)
builder.assign(Data::someProperty, "some value") // (1)
builder.niceAssign { ::someProperty to "some value" } // (2)
Since builder object is generified with Data class, I don't really need to explicitly indicate Data class while passing a property reference. Assign method already knows which class that property belongs to. So I don't want write "Data::" every time in assign method (like in code (1)), but I want to pass "Data::" as a receiver property for niceAssign param, so I could reference ::someProperty from "this" object.
This code snippet doesn't work because I'm passing KClass as a receiver, and KClass doesn't have property references of T.
So, is there any way to make it work?
If you want to pass property reference to a higher-order function, then it should be a function with a T as a receiver.
In the described case it need to be:
fun <K> niceAssign(call: T.() -> Pair<KProperty0<K>, K>) : Builder<T> {
...
}
But to "unpack" passed to niceAssign arguments (so that they could be passed further to assign function) you will need an instance of T, like:
val (prop, value) = call(kClass.createInstance())
Also result couldn't be passed to assign method as is, since prop parameter type won't match, so this approach will require some additional code rework.

Get KProperty of a non-package extension property

In kotlin, you can use the reference operator to get the KProperty of a package extension property like this:
val String.extProp: String
get() = "Some get code"
fun foo() {
val prop: KProperty<String> = String::extProp
}
However, when the extension property is declared inside a class the reference operator no longer works:
class Example() {
val String.extProp: String
get() = "Some get code"
fun foo() {
val prop: KProperty<String> = String::extProp // error
}
}
So what I am wondering is how can I change the problematic line in the second example, so the KProperty is gotten?
The error you are getting is:
Error:(y, x) Kotlin: 'extProp' is a member and an extension at the same time. References to such elements are not allowed
There is no syntax mechanism to generate a reference to an extension method that also requires a containing class. Your extension for example might use members of the class, and this would need something like "bound references" coming in Kotlin 1.1 (which I'm not sure will cover this case either, it is currently an open question). So for now, there is no :: syntax available. Things like Example::String::extProp are not available, neither is the commonly tried Example::String.extProp syntax. But you can find it by reflection.
First you need to know the type you will receive is:
KProperty2<INSTANCE, EXTENDING, PROPTYPE>
Whereas a normal property on a class is:
KProperty1<INSTANCE, PROPTYPE>
You need to know that because any call to the getter will require the class instance and an instance of the class the property is extending. So you cannot call it the same way as you would a property reference of a class.
You can use this function to find an extension property declared in a class:
#Suppress("UNCHECKED_CAST")
fun <T: Any, EXTENDING: Any, R: Any> KClass<T>.extProp(extends: KClass<EXTENDING>, name: String, returning: KClass<R>): KProperty2<T, EXTENDING, R> {
return this.declaredMemberExtensionProperties.first {
it.name == name &&
it.parameters.size == 2 &&
it.parameters[0].kind == KParameter.Kind.INSTANCE && it.parameters[0].type == this.defaultType &&
it.parameters[1].kind == KParameter.Kind.EXTENSION_RECEIVER && it.parameters[1].type == extends.defaultType &&
it.returnType == returning.defaultType
} as KProperty2<T, EXTENDING, R>
}
This is a bit overkill for the checking but ensures that it is future-proof in case any other types of extensions are added later. The following is your code updated to use it:
class Example() {
val String.extProp: String
get() = "howdy $this"
fun foo() {
val prop = Example::class.extProp(String::class, "extProp", String::class)
println(prop.get(this, "stringy")) // "howdy stringy"
}
}

How can I assign a value to KMutableProperty parameter?

In a method I would like to receive KMutableProperty as parameter and assign a value to it.
Another question is what is the correct way of passing a parameter into such a method.
Basically I would like to have something like that:
class MyBinder {
...
fun bind(property: KMutableProperty<Int>): Unit {
property.set(internalIntValue)
}
}
And then call it in another class
myBinder.bind(this::intProperty)
Kotlin 1.0 does not allow the this::intProperty syntax, but this is being worked currently and will be available soon as a part of the early access preview of 1.1 (issue, KEEP proposal).
With this in mind, I'd consider doing what you're describing in another way, for example making bind accept a lambda which sets the property:
class MyBinder {
fun bind(setProperty: (Int) -> Unit) {
setProperty(internalIntValue)
}
}
...
myBinder.bind { intProperty = it }
Anyway, to answer your question about setting the value of KMutableProperty: to set the value of some property or, technically speaking, to invoke the property setter, you should know its arity, or the number of parameters that property (and its getter/setter) accepts. Properties declared in a file do not accept any parameters, member properties and extension properties require one parameter (the receiver instance), while member properties which are also extensions take two parameters. These kinds of properties are represented by the following subtypes of KMutableProperty respectively: KMutableProperty0, KMutableProperty1, KMutableProperty2 -- the number means the arity and their generic type parameters mean the types of receivers. Each of these property types has a set method with the corresponding parameters. Some examples:
fun setValue(property: KMutableProperty0<Int>, value: Int) {
property.set(value)
}
fun setValue(property: KMutableProperty1<SomeType, Int>, instance: SomeType, value: Int) {
property.set(instance, value)
}
Note that there's no set (or get) method in the abstract KMutableProperty interface precisely because it's impossible to declare it, not knowing the number of required receiver parameters.
Additionally to Alexander's answer, you can try something like this:
import kotlin.reflect.KMutableProperty
class Binder {
val internalIntValue = 10
fun bind(self: Any, aProperty: KMutableProperty<Int>) {
aProperty.setter.call(self, internalIntValue)
}
}
class Foo {
var bar = 1
fun changeBar() {
Binder().bind(this, Foo::bar)
}
}
fun main(args: Array<String>) {
val foo = Foo()
assert(1 == foo.bar)
foo.changeBar()
assert(10 == foo.bar)
}
A more robust/safe way to do the same thing:
fun <T> bind(self: T, aProperty: KMutableProperty1<T, Int>) {
aProperty.set(self, internalIntValue)
}
My thanks to Alexander. His answer gave me the previous idea.