Getting member variables through reflection in Kotlin - reflection

If I have a class in Kotlin:
class Foo{
var x= null
var y=null
}
I want to check which of these members have been set at runtime through reflection. I can iterate over them and check for null in Java.
Foo foo= new Foo();
//this gives me the value of foo.x
Foo.class.getDeclaredField("x").get(foo);
How can I do the same in Kotlin/Native? I know I can achieve this in Android by
Foo::class.java.getDeclaredField("x").get(foo)
But this doesn't work in native environment.

I'm just going by the documentation, so the below may be a bit wrong, but you could try this:
val prop : KCallable = Foo::class.members.firstOrNull { it.name == "x" }
if (prop != null) {
val xValue : Int? = prop.call(object)
//you have to declare the type of the xValue
}

Related

Convert Firebase DocumentSnapshot data to a custom Scala (or Java) class

I'm using Firebase for a private Scala project and I'm struggling to understand how I can manage Firebase responses if I want to avoid using HashMap.
This is the information that I'm trying to manage:
These are the two Scala classes that I wrote with the idea to use them along with the toObject method:
class Doc() {
#BeanProperty val map: util.HashMap[String, Value] = new util.HashMap[String, Value]()
}
class Value() {
#BeanProperty val displayName: String = ""
#BeanProperty val email: String = ""
// Extra fields that I need to initialize in the Scala code
#BeanProperty val totalLogins: Int = 0
#BeanProperty val todoMap: util.HashMap[String, String] = new util.HashMap[String, String]()
#BeanProperty val todoList: util.ArrayList[String] = new util.ArrayList[String]()
#BeanProperty val totalChanges: Int = 0
#BeanProperty val totalErrors: Int = 0
}
And this is snapshot listener implementation that I wrote:
docFirebase.addSnapshotListener(new EventListener[DocumentSnapshot]() {
override def onEvent(snapshot: DocumentSnapshot, e: FirestoreException): Unit = {
if (e != null) {
println("[OnSnapshot] Listen failed: " + e)
return
}
if (snapshot != null && snapshot.exists) {
val doc = snapshot.toObject(classOf[Doc])
// Here below I'll write the complex logic I need ...
} else {
println("[OnSnapshot] Current data: null")
}
}
})
Using that code I'm always getting an empty HashMap into the doc variable. Can someone helps me understand what I misunderstood about reading data from Firebase ? Thanks in advance.
All of your properties in the document are nested under an object called "abc". That fact is not reflected in your code - you need to call out abc by name to get all the nested fields from it. You probably don't want that nesting at all. Just put all the fields (displayName, email, etc) as top-level fields in the document.

Type check fails for memberProperty returnType superclass

I've got an instance of an object, which I scan for memberProperties that have a proper annotation attached on them. Then, I want to filter based on their return type.
For example if declaration is as follows: class AutoValidatedThing : AutoValidatedUserInputComponent {...} and the target instance contains a #ValidComponent val someProperty: AutoValidatedThing = ..., I'd want to get the someProperty as a AutoValidatedUserInputComponent to the end of the following code block:
val invalidOnes = this::class.memberProperties
.filter { it.javaField != null && it.javaField!!.isAnnotationPresent(ValidComponent::class.java) }
.filter { val annotations = it.javaField?.annotations; annotations != null
&& annotations.map { ann -> ann.annotationClass }.contains(ValidComponent::class)
&& it.returnType is AutoValidatedUserInputComponent }
.map { it.getter.call() as AutoValidatedUserInputComponent }
But it.returnType is AutoValidatedUserInputComponent ALWAYS returns false.
AutoValidatedUserInputComponent is a simple interface:
interface AutoValidatedUserInputComponent {
fun blabla() : SomeType
}
Calling returnType on a KProperty doesn't return an instance with the given type that you could do an is check against - it returns a reflection class describing the type, specificallyKType, which of course does not implement your interface. Instead of using is, you can call isSubTypeOf on it, and check if it's a subtype of another given KType.
For that call, you'll need to get a KType for your own interface - for this, you can use createType on its KClass:
val targetType = AutoValidatedUserInputComponent::class.createType(nullable = true)
The nullability part is up to you, and there are also other optional parameters for createType, if your interface would happen to have type parameters, for example.
Then, as I mentioned, you can use isSubTypeOf:
val invalidOnes = this::class.memberProperties
.filter { it.javaField != null && it.javaField!!.isAnnotationPresent(ValidComponent::class.java) }
.filter {
val annotations = it.javaField?.annotations
annotations != null
&& annotations.map { ann -> ann.annotationClass }.contains(ValidComponent::class)
&& it.returnType.isSubtypeOf(targetType)
}
.forEach {
println("Found field with annotation and given supertype: $it")
}

Call data class copy() via reflection

Is it possible to call the copy() function of a data class via reflection in Kotlin? How can I get a reference to the function itself? Is there a superclass for all data classes?
There's no common supertype for all data classes.
Basically, copy is a normal member function which you can call with the Kotlin reflection API as follows:
val person = Person("Jane", 23)
val copy = person::class.memberFunctions.first { it.name == "copy" }
val instanceParam = copy.instanceParameter!!
val ageParam = copy.parameters.first { it.name == "age" }
val result = copy.callBy(mapOf(instanceParam to person, ageParam to 18))
println(result) // Person(name=Jane, age=18)
Make sure you add kotlin-reflect as a dependency.
The example above shows how to omit values for the default parameters – no value is passed for name. If you want to pass all the parameters, this can be done in a simpler way:
val person = Person("Jane", 23)
val copy = person::class.memberFunctions.first { it.name == "copy" }
val result = copy.call(person, person.name, 18)
println(result) // Person(name=Jane, age=18)
Kotlin reflection API is not strictly necessary to call a function if you pass arguments for all of the parameters, you can do that via Java reflection as well:
val person = Person("Jane", 23)
val copy = person::class.java.methods.first { it.name == "copy" }
val result = copy.invoke(person, person.name, 18)
println(result) // Person(name=Jane, age=18)
So, based on https://stackoverflow.com/users/2196460/hotkey's answer above:
fun <T : Any> clone (obj: T): T {
if (!obj::class.isData) {
println(obj)
throw Error("clone is only supported for data classes")
}
val copy = obj::class.memberFunctions.first { it.name == "copy" }
val instanceParam = copy.instanceParameter!!
return copy.callBy(mapOf(
instanceParam to obj
)) as T
}

How can I get the name of a Kotlin property?

I have the following function to access a property's delegate. It uses Kotlin reflection to get a property's name and Java reflection to get the field.
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
The method is used like this:
val delegate = a.getDelegate(A::b)
However, I would prefer to use it like this:
val delegate = a.b.delegate
The problem with the code above is getting the property name of a.b and getting the instance a from a.b. From what I know about Kotlin, this is probably not possible, however I'd like to see if I can clean up my function at all.
To give a bigger picture of what I'm trying do here's my complete code. I want an observable delegate to which I can add and remove observers using the delegate reference and without creating addition variables.
fun Any.addObservable<T>(prop: KProperty<T>, observer: (T) -> Unit) {
getObservableProperty(prop).observers.add(observer)
}
fun Any.getObservableProperty<T>(prop: KProperty<T>): ObservableProperty<T> {
return getDelegate(prop) as ObservableProperty<T>
}
fun Any.getDelegate<T>(prop: KProperty<T>): Any {
return javaClass.getDeclaredField("${prop.name}\$delegate").let {
it.setAccessible(true)
it.get(this)
}
}
class ObservableProperty<T>(
initialValue: T,
initialObservers: Array<(T) -> Unit> = emptyArray()) : ReadWriteProperty<Any?, T> {
private var value = initialValue
public val observers: MutableSet<(T) -> Unit> = initialObservers.toHashSet()
public override fun get(thisRef: Any?, desc: PropertyMetadata): T {
return value
}
public override fun set(thisRef: Any?, desc: PropertyMetadata, value: T) {
this.value = value
observers.forEach { it(value) }
}
}
class A() {
var b by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(A::b) {
println("b is now $it")
}
a.b = 1
a.b = 2
a.b = 3
}
Edit:
I just realized that the function also isn't strict because the property delegate field name is referenced by KProperty name, which doesn't require a strong reference to the enclosing class. Here's an example to demonstrate the problem:
class A() {
var foo by ObservableProperty(0)
}
class B() {
var foo by ObservableProperty(0)
}
fun main(args: Array<String>) {
val a = A()
a.addObservable(B::foo) {
println("b is now $it")
}
a.foo = 1
a.foo = 2
a.foo = 3
}
This compiles and runs without error because A::foo and B::foo both result in a field string of "foo$delegate.
Right now reflection is all we can do to get to the delegate object. We are designing a language feature to have direct access to delegate instance, but it's long way to go.
This is how you get the name of a Kotlin Property (although only with an instance of the class). This part will be useful to anyone arriving at this question purely based off its title.
class Stuff(val thing: String)
val stuff = Stuff("cool stuff")
val thingFieldName = "${stuff.thing}\$delegate"
// value of thingFieldName is now "thing"
In terms of getting the delegate itself easier, they say you can now do this:
class Foo {
var bar: String by ReactiveProperty<String>()
}
val foo = Foo()
val bar = foo.bar
val barDelegate = ... // foo.bar$delegate
See ticket.

flex/actionscript assignment failing?

I'm seeing something weird in my actionscript code
I have two classes foo and bar, bar extends foo. In a model class I have a foo member variable, I assign an bar object to the foo variable. But after the assignment the foo variable is null.
[Bindable] public var f:foo;
public function someFunc(arr:ArrayCollection):void {
if(arr.length > 0) {
var tempBar:bar = arr.getItemAt(0) as bar;
if(tempBar != null) {
tempBar.someProp++;
f = tempBar;
// f is now null
}
}
}
Any ideas on what I could be doing wrong?
EDIT
Here is the exact code:
[Bindable] public var selectedCustomerJob:IDSCustomer;
public function selectedJobByIdCallback(evt:Event):void
{
var temp:IDSDTOArrayCollection = evt.currentTarget as IDSDTOArrayCollection;
if(null != temp && temp.length > 0)
{
selectedCustomerJob = IDSJob(temp.getItemAt(0));;
trace(" selectedCustomerJob: " + flash.utils.getQualifiedClassName(selectedCustomerJob));
trace(" jobToSelect type: " + flash.utils.getQualifiedClassName(temp.getItemAt(0)));
trace("jobToSelect super class: " + flash.utils.getQualifiedSuperclassName(temp.getItemAt(0)));
}
}
this is the trace output:
selectedCustomerJob: null
jobToSelect type: com.intuit.sb.cdm.v2::IDSJob
jobToSelect super class: com.intuit.sb.cdm.v2::IDSCustomer
Casting using the as keyword returns null when it fails. In this case, the first item in the array collection may not be an object of type Bar as you expect; it could be a Foo or something else. You can cast subclass object to base-class but not the other way.
Use the parenthesis syntax for casting - it'll throw an exception if cast fails and thus you can figure out the type of arr.getItemAt(0).
//Change
var tempBar:Bar = arr.getItemAt(0) as Bar;
//to
var tempBar:Bar = Bar(arr.getItemAt(0));
to make sure that the first item in the array collection is indeed a Bar instance (and not Foo or something else).
Otherwise you can find the type using
trace(flash.utils.getQualifiedClassName(arr.getItemAt(0)));
if(tempBar != null) {
tempBar.someProp++;
f = tempBar;
// f is now null
}
By the way, I believe the posted code is not the exact code you ran because for f to be null, tempBar should be null as you assigning it to f. In that case the code inside if should not be executed as you're checking for null inside if. Even if it enters the if block, it will throw a null pointer error (#1009) in the first line where you're trying to increment tempBar.someProp

Resources