Call data class copy() via reflection - 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
}

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.

Getting member variables through reflection in Kotlin

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
}

How to change a member field with Kotlin reflection?

I'm porting a class from Java to Kotlin. This class declares hundreds of objects. Each object has a name property which is identical with the declared variable name of the object. Java reflection allows to use the declared name via reflection to set the object member name. Just saves one parameter in hundreds of constructors.
I try to do the same in Kotlin but can't figure out how to do the property setting. Here is some simplified test code:
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.declaredMemberProperties
class MyTestObject() {
var name: String = "NotInitialized"
companion object {
val Anton = MyTestObject()
val Berta = MyTestObject()
val Caesar = MyTestObject()
}
}
fun main(args : Array<String>) {
println(MyTestObject.Anton.name) // name not yet initialized
// Initialize 'name' with the variable name of the object:
for (member in MyTestObject::class.companionObject!!.declaredMemberProperties) {
if (member.returnType.toString() == "myPackage.MyTestObject") {
println("$member: ${member.name}")
// Set 'name' property to 'member.name':
// ???
}
}
println(MyTestObject.Anton.name) // now with the initialized name
}
The ??? line is where I would like to get access to the name property of MyTestObject to set it to to member.name. I'm looking for a function similar to (member.toObject() as MyTestObject).name = member.name.
While kotlin-reflection strives to be type-safe, sometimes the type system and the inference logic are not enough to allow for the things like what you are trying to do in a type-safe way. So, you have to make unchecked casts, stating that your knowledge about the types is more than the compiler can infer.
In your case, it's enough to cast member so that you can pass the companion object instance into its .get(...) and use the result as a MyTestObject, replace the // ??? line with:
#Suppress("UNCHECKED_CAST")
(member as KProperty1<Any, MyTestObject>)
.get(MyTestObject::class.companionObject!!.objectInstance!!)
.name = member.name
If you can replace MyTestObject::class.companionObject!! with MyTestObject.Companion::class (i.e. your actual use case does not involve getting .companionObject from different classes), the unchecked cast is not needed, and you can replace the statement above with this:
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
As an alternative that does not require companion object reflection at all, you can do the same binding logic with the delegation. Implementing provideDelegate allows you to customize the logic of initializing the property, and that's where you can assign the names:
operator fun MyTestObject.provideDelegate(
thisRef: MyTestObject.Companion,
property: KProperty<*>
) = apply { name = property.name }
operator fun MyTestObject.getValue(
thisRef: MyTestObject.Companion,
property: KProperty<*>
) = this
Then declare your properties as
val Anton by MyTestObject()
val Berta by MyTestObject()
val Caesar by MyTestObject()
Here is the final test code based on hotkey's solution:
package myPackage
import kotlin.reflect.full.declaredMemberProperties
class MyTestObject() {
lateinit var name: String
companion object {
val Anton = MyTestObject()
val Berta = MyTestObject()
val Caesar = MyTestObject()
init {
for (member in MyTestObject.Companion::class.declaredMemberProperties) {
if (member.returnType.toString() == "myPackage.MyTestObject") {
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
}
}
}
}
}
fun main(args : Array<String>) {
println(MyTestObject.Anton.name)
println(MyTestObject.Caesar.name)
}

How to convert/transform a collection to another collection by element's property?

If I have a collection of an object in Kotlin, is there a quick way to get a collection of a certain property of those objects? I looked at a list of collection operations for Kotlin, but nothing stood out for me (but I may have overlooked something)
In python it would be akin to:
[person.name for person in persons]
And I'd prefer to use a collections function instead of doing:
var nameMap = mutableListOf<String>()
persons.forEach{person -> nameMap.add(person.name)}
I'm pretty lacking in knowledge of filtering/lambda functions and anything other than list comprehension, so apologies if this is a simple question
it's easy to do in Kotlin:
// v--- the variable type can be removed
var nameMap: MutableList<String> = persons.map { it.name }.toMutableList();
IF you want an immutable List, it can simplify as below:
// v--- the variable type can be removed
var nameMap: List<String> = persons.map { it.name };
OR using function reference expression instead:
var nameMap = persons.map(Person::name);
If you want to map model then do this.
var nameMap: List<Model> = persons.map { Model(it.name,it.number) };
Option 1:
inline fun <reified T, Y> MutableList<T>.listOfField(property: KMutableProperty1<T, Y?>): MutableList<Y> {
val yy = ArrayList<Y>()
this.forEach { t: T ->
yy.add(property.get(t) as Y)
}
return yy
}
Usage:
val serviceIds = services.listOfField(ServiceModel::id)
Option 2:
var serviceIds: MutableList<String> = services.map { it.id }.toMutableList()
Option 3:
Function Reference
var serviceIds = services.map(Person::id)

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.

Resources