Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call data class copy() via reflection

That's it. Is it possible to call the copy() function of a data class via reflection in Kotlin? How can I have a reference to the function itself? Is there a superclass for all data classes? (I can't find it...)

like image 739
Joao Neto Avatar asked Mar 27 '18 10:03

Joao Neto


2 Answers

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)
like image 99
hotkey Avatar answered Oct 04 '22 13:10

hotkey


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
}


like image 43
Kirill Groshkov Avatar answered Oct 04 '22 11:10

Kirill Groshkov