Kotlin data class copy method not deep copying all members - kotlin

Could someone explain how exactly the copy method for Kotlin data classes work? It seems like for some members, a (deep) copy is not actually created and the references are still to the original.
fun test() {
val bar = Bar(0)
val foo = Foo(5, bar, mutableListOf(1, 2, 3))
println("foo : $foo")
val barCopy = bar.copy()
val fooCopy = foo.copy()
foo.a = 10
bar.x = 2
foo.list.add(4)
println("foo : $foo")
println("fooCopy: $fooCopy")
println("barCopy: $barCopy")
}
data class Foo(var a: Int,
val bar: Bar,
val list: MutableList<Int> = mutableListOf())
data class Bar(var x: Int = 0)
Output:
foo : Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
foo : Foo(a=10, bar=Bar(x=2), list=[1, 2, 3, 4])
fooCopy: Foo(a=5, bar=Bar(x=2), list=[1, 2, 3, 4])
barCopy: Bar(x=0)
Why is barCopy.x=0 (expected), but fooCopy.bar.x=2 (I would think it would be 0). Since Bar is also a data class, I would expect foo.bar to also be a copy when foo.copy() is executed.
To deep copy all members, I can do something like this:
val fooCopy = foo.copy(bar = foo.bar.copy(), list = foo.list.toMutableList())
fooCopy: Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
But am I missing something or is there a better way to do this without needing to specify that these members need to force a deep copy?

The copy method of Kotlin is not supposed to be a deep copy at all. As explained in the reference doc (https://kotlinlang.org/docs/reference/data-classes.html), for a class such as:
data class User(val name: String = "", val age: Int = 0)
the copy implementation would be:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
So as you can see, it's a shallow copy. The implementations of copy in your specific cases would be:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list) = Foo(a, bar, list)
fun copy(x: Int = this.x) = Bar(x)

Beware of those answers who are just copying list reference from an old object into the new one. One quick way (not very efficient, though) of deep copying is to serialize/deserialize objects i.e. convert the objects into JSON and then transform them back to POJO.
If you are using GSON, here is a quick piece of code:
class Foo {
fun deepCopy() : Foo {
return Gson().fromJson(Gson().toJson(this), this.javaClass)
}
}

As #Ekeko said, the default copy() function implemented for data class is a shallow copy which looks like this:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list)
To perform a deep copy, you have to override the copy() function.
fun copy(a: Int = this.a, bar: Bar = this.bar.copy(), list: MutableList<Int> = this.list.toList()) = Foo(a, bar, list)

There is a way to make a deep copy of an object in Kotlin (and Java): serialize it to memory and then deserialize it back to a new object. This will only work if all the data contained in the object are either primitives or implement the Serializable interface
Here is an explanation with sample Kotlin code https://rosettacode.org/wiki/Deepcopy#Kotlin
import java.io.Serializable
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectOutputStream
import java.io.ObjectInputStream
fun <T : Serializable> deepCopy(obj: T?): T? {
if (obj == null) return null
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
val bais = ByteArrayInputStream(baos.toByteArray())
val ois = ObjectInputStream(bais)
#Suppress("unchecked_cast")
return ois.readObject() as T
}
Note: This solution should also be applicable in Android using the Parcelable interface instead of the Serializable. Parcelable is more efficient.

Building on a previous answer, an easy if somewhat inelegant solution is to use the kotlinx.serialization facility. Add the plugin to build.gradle as per the docs, then to make a deep copy of an object, annotate it with #Serializable and add a copy method which converts the object to a serialised binary form, then back again. The new object will not reference any objects in the original.
import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor
#Serializable
data class DataClass(val yourData: Whatever, val yourList: List<Stuff>) {
var moreStuff: Map<String, String> = mapOf()
fun copy(): DataClass {
return Cbor.load(serializer(), Cbor.dump(serializer(), this))
}
This won't be as fast as a handwritten copy function, but it does not require updating if the object is changed, so is more robust.

I face the same problem. Because in kotlin, ArrayList.map {it.copy} not copying all items of an object specially if a member is list of another object inside this.
The only solution, for deep copying of all items of an object I found on the web, is to serialize and deserialize the object when you send or assign it to a new variable. Code like as follows.
#Parcelize
data class Flights(
// data with different types including the list
) : Parcelable
Before I receiving List of Flights, We can use JSON to deserialize the Object and serialize the object same time!!!.
First, we create two extension functions.
// deserialize method
fun flightListToString(list: ArrayList<Flights>): String {
val type = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().toJson(list, type)
}
// serialize method
fun toFlightList(string: String): List<Flights>? {
val itemType = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().fromJson<ArrayList<Flights>>(string, itemType)
}
We can use it like below.
// here I assign list from Navigation args
private lateinit var originalFlightList: List<Flights>
...
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
originalFlightList = toFlightList(flightListToString(temporaryList))!!
Later, I send this list to Recycler Adapter & there the content of the Flights object would be modified.
bindingView.imageViewReset.setOnClickListener {
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
val flightList = toFlightList(flightListToString(temporaryList))!!
**adapter**.resetListToOriginal(flightList)
}

Maybe you can use kotlin reflection in some way here, this example is not recursive but should give the idea:
fun DataType.deepCopy() : DataType {
val copy = DataType()
for (m in this::class.members) {
if (m is KProperty && m is KMutableProperty) {
m.setter.call(copy, if (m.returnType::class.isData) {
(m.getter.call(this) to m.returnType).copy()
} else m.setter.call(copy, m.getter.call(this)))
}
}
return copy
}

If you use Jackson and not concerned about performance,then this simple extension function will give you this feature.
private val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
fun <T> Any.copyDeep(): T {
return objectMapper.readValue(objectMapper.writeValueAsString(this), this.javaClass) as T
}

Use this function:
private val gson = Gson()
fun <T> deepCopy(item: T?, clazz: Class<T>): T {
val str = gson.toJson(item)
return gson.fromJson(str, clazz)
}

What you want is a deep copy. There are many tools available to do this.
MapStruct: https://mapstruct.org/
Mapstruct generates code at compile time. Normally, it is to auto-generate mappers between java objects, but it also has a 'clone' functionality to create a deep copy of an object. Since this is generated code of what you'd manually write, it is the fastest way to achieve this.
There are many more (kryo, dozer, etc...), you can actually just google, for example here: https://programmer.group/performance-comparison-between-shallow-and-deep-copies.html
DO AVOID serialization-based 'clone': apache commons' SerializationUtils, jackson, gson, etc... They have a huge overhead, since it first creates a middle state. They are about 10-100 times slower than actual copying.

Related

Kotlin — creating new instance with copy() does not prevent methods called on copied instance from modifying original instance [duplicate]

Could someone explain how exactly the copy method for Kotlin data classes work? It seems like for some members, a (deep) copy is not actually created and the references are still to the original.
fun test() {
val bar = Bar(0)
val foo = Foo(5, bar, mutableListOf(1, 2, 3))
println("foo : $foo")
val barCopy = bar.copy()
val fooCopy = foo.copy()
foo.a = 10
bar.x = 2
foo.list.add(4)
println("foo : $foo")
println("fooCopy: $fooCopy")
println("barCopy: $barCopy")
}
data class Foo(var a: Int,
val bar: Bar,
val list: MutableList<Int> = mutableListOf())
data class Bar(var x: Int = 0)
Output:
foo : Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
foo : Foo(a=10, bar=Bar(x=2), list=[1, 2, 3, 4])
fooCopy: Foo(a=5, bar=Bar(x=2), list=[1, 2, 3, 4])
barCopy: Bar(x=0)
Why is barCopy.x=0 (expected), but fooCopy.bar.x=2 (I would think it would be 0). Since Bar is also a data class, I would expect foo.bar to also be a copy when foo.copy() is executed.
To deep copy all members, I can do something like this:
val fooCopy = foo.copy(bar = foo.bar.copy(), list = foo.list.toMutableList())
fooCopy: Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
But am I missing something or is there a better way to do this without needing to specify that these members need to force a deep copy?
The copy method of Kotlin is not supposed to be a deep copy at all. As explained in the reference doc (https://kotlinlang.org/docs/reference/data-classes.html), for a class such as:
data class User(val name: String = "", val age: Int = 0)
the copy implementation would be:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
So as you can see, it's a shallow copy. The implementations of copy in your specific cases would be:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list) = Foo(a, bar, list)
fun copy(x: Int = this.x) = Bar(x)
Beware of those answers who are just copying list reference from an old object into the new one. One quick way (not very efficient, though) of deep copying is to serialize/deserialize objects i.e. convert the objects into JSON and then transform them back to POJO.
If you are using GSON, here is a quick piece of code:
class Foo {
fun deepCopy() : Foo {
return Gson().fromJson(Gson().toJson(this), this.javaClass)
}
}
As #Ekeko said, the default copy() function implemented for data class is a shallow copy which looks like this:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list)
To perform a deep copy, you have to override the copy() function.
fun copy(a: Int = this.a, bar: Bar = this.bar.copy(), list: MutableList<Int> = this.list.toList()) = Foo(a, bar, list)
There is a way to make a deep copy of an object in Kotlin (and Java): serialize it to memory and then deserialize it back to a new object. This will only work if all the data contained in the object are either primitives or implement the Serializable interface
Here is an explanation with sample Kotlin code https://rosettacode.org/wiki/Deepcopy#Kotlin
import java.io.Serializable
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectOutputStream
import java.io.ObjectInputStream
fun <T : Serializable> deepCopy(obj: T?): T? {
if (obj == null) return null
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
val bais = ByteArrayInputStream(baos.toByteArray())
val ois = ObjectInputStream(bais)
#Suppress("unchecked_cast")
return ois.readObject() as T
}
Note: This solution should also be applicable in Android using the Parcelable interface instead of the Serializable. Parcelable is more efficient.
Building on a previous answer, an easy if somewhat inelegant solution is to use the kotlinx.serialization facility. Add the plugin to build.gradle as per the docs, then to make a deep copy of an object, annotate it with #Serializable and add a copy method which converts the object to a serialised binary form, then back again. The new object will not reference any objects in the original.
import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor
#Serializable
data class DataClass(val yourData: Whatever, val yourList: List<Stuff>) {
var moreStuff: Map<String, String> = mapOf()
fun copy(): DataClass {
return Cbor.load(serializer(), Cbor.dump(serializer(), this))
}
This won't be as fast as a handwritten copy function, but it does not require updating if the object is changed, so is more robust.
I face the same problem. Because in kotlin, ArrayList.map {it.copy} not copying all items of an object specially if a member is list of another object inside this.
The only solution, for deep copying of all items of an object I found on the web, is to serialize and deserialize the object when you send or assign it to a new variable. Code like as follows.
#Parcelize
data class Flights(
// data with different types including the list
) : Parcelable
Before I receiving List of Flights, We can use JSON to deserialize the Object and serialize the object same time!!!.
First, we create two extension functions.
// deserialize method
fun flightListToString(list: ArrayList<Flights>): String {
val type = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().toJson(list, type)
}
// serialize method
fun toFlightList(string: String): List<Flights>? {
val itemType = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().fromJson<ArrayList<Flights>>(string, itemType)
}
We can use it like below.
// here I assign list from Navigation args
private lateinit var originalFlightList: List<Flights>
...
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
originalFlightList = toFlightList(flightListToString(temporaryList))!!
Later, I send this list to Recycler Adapter & there the content of the Flights object would be modified.
bindingView.imageViewReset.setOnClickListener {
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
val flightList = toFlightList(flightListToString(temporaryList))!!
**adapter**.resetListToOriginal(flightList)
}
Maybe you can use kotlin reflection in some way here, this example is not recursive but should give the idea:
fun DataType.deepCopy() : DataType {
val copy = DataType()
for (m in this::class.members) {
if (m is KProperty && m is KMutableProperty) {
m.setter.call(copy, if (m.returnType::class.isData) {
(m.getter.call(this) to m.returnType).copy()
} else m.setter.call(copy, m.getter.call(this)))
}
}
return copy
}
If you use Jackson and not concerned about performance,then this simple extension function will give you this feature.
private val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
fun <T> Any.copyDeep(): T {
return objectMapper.readValue(objectMapper.writeValueAsString(this), this.javaClass) as T
}
Use this function:
private val gson = Gson()
fun <T> deepCopy(item: T?, clazz: Class<T>): T {
val str = gson.toJson(item)
return gson.fromJson(str, clazz)
}
What you want is a deep copy. There are many tools available to do this.
MapStruct: https://mapstruct.org/
Mapstruct generates code at compile time. Normally, it is to auto-generate mappers between java objects, but it also has a 'clone' functionality to create a deep copy of an object. Since this is generated code of what you'd manually write, it is the fastest way to achieve this.
There are many more (kryo, dozer, etc...), you can actually just google, for example here: https://programmer.group/performance-comparison-between-shallow-and-deep-copies.html
DO AVOID serialization-based 'clone': apache commons' SerializationUtils, jackson, gson, etc... They have a huge overhead, since it first creates a middle state. They are about 10-100 times slower than actual copying.

Generic inline function

Let's say I have an object which helps me to deserialize other objects from storage:
val books: MutableList<Book> = deserializer.getBookList()
val persons: MutableList<Person> = deserializer.getPersonList()
The methods getBookList and getPersonList are extension functions I have written. Their logic is allmost the same so I thought I may can combine them into one method. My problem is the generic return type. The methods look like this:
fun DataInput.getBookList(): MutableList<Book> {
val list = mutableListOf<Book>()
val size = this.readInt()
for(i in 0 .. size) {
val item = Book()
item.readExternal(this)
list.add(item)
}
return list
}
Is there some Kotlin magic (maybe with inline functions) which I can use to detect the List type and generify this methods? I think the problem would be val item = T() which will not work for generic types, right? Or is this possible with inline functions?
You cannot call the constructor of a generic type, because the compiler can't guarantee that it has a constructor (the type could be from an interface). What you can do to get around this though, is to pass a "creator"-function as a parameter to your function. Like this:
fun <T> DataInput.getList(createT: () -> T): MutableList<T> {
val list = mutableListOf<T>()
val size = this.readInt()
for(i in 0 .. size) {
val item = createT()
/* Unless readExternal is an extension on Any, this function
* either needs to be passed as a parameter as well,
* or you need add an upper bound to your type parameter
* with <T : SomeInterfaceWithReadExternal>
*/
item.readExternal(this)
list.add(item)
}
return list
}
Now you can call the function like this:
val books: MutableList<Book> = deserializer.getList(::Book)
val persons: MutableList<Person> = deserializer.getList(::Person)
Note:
As marstran mentioned in a comment, this requires the class to have a zero-arg constructor to work, or it will throw an exception at runtime. The compiler will not warn you if the constructor doesn't exist, so if you pick this way, make sure you actually pass a class with a zero-arg constructor.
You can't initialize generic types, in Kotlin or Java. At least not in the "traditional" way. You can't do this:
val item = T()
In Java, you'd pass a Class<T> and get the constructor. Very basic example of that:
public <T> void x(Class<T> cls){
cls.getConstructor().newInstance(); // Obviously you'd do something with the return value, but this is just a dummy example
}
You could do the same in Kotlin, but Kotlin has a reified keyword that makes it slightly easier. This requires an inline function, which means you'd change your function to:
inline fun <reified T> DataInput.getBookList(): MutableList<T> { // Notice the `<reified T>`
val list = mutableListOf<T>() // Use T here
val size = this.readInt()
for(i in 0 .. size) {
// This is where the initialization happens; you get the constructor, and create a new instance.
// Also works with arguments, if you have any, but you used an empty one so I assume yours is empty
val item = T::class.java.getConstructor().newInstance()!!
item.readExternal(this) // However, this is tricky. See my notes below this code block
list.add(item)
}
return list
}
However, readExternal isn't present in Any, which will present problems. The only exception is if you have an extension function for either Any or a generic type with that name and input.
If it's specific to some classes, then you can't do it like this, unless you have a shared parent. For an instance:
class Book(){
fun readExternal(input: DataInput) { /*Foo bar */}
}
class Person(){
fun readExternal(input: DataInput) { /*Foo bar */}
}
Would not work. There's no shared parent except Any, and Any doesn't have readExternal. The method is manually defined in each of them.
You could create a shared parent, as an interface or abstract class (assuming there isn't one already), and use <reified T : TheSharedParent>, and you would have access to it.
You could of course use reflection, but it's slightly harder, and adds some exceptions you need to handle. I don't recommend doing this; I'd personally use a superclass.
inline fun <reified T> DataInput.getBookList(): MutableList<T> {
val list = mutableListOf<T>()
val size = this.readInt()
val method = try {
T::class.java.getMethod("readExternal", DataInput::class.java)
}catch(e: NoSuchMethodException){
throw RuntimeException()
}catch(e: SecurityException){
throw RuntimeException()// This could be done better; but error handling is up to you, so I'm just making a basic example
// The catch clauses are pretty self-explanatory; if something happens when trying to get the method itself,
// These two catch them
}
for(i in 0 .. size) {
val item: T = T::class.java.getConstructor().newInstance()!!
method.invoke(item, this)
list.add(item)
}
return list
}

kotlin custom get immutable list from mutableList

I have a custom getter method for a mutable list to return an immtuable list by using Google's Guava library. And then this mutable list is accessed in the constructor.
data class mutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = mutableListOf()
list.forEach {
mutableList.add(it.copy()) // Exception is thrown here.
// It actually calls its getter method which is an immutable
// list, so when init this class, it throw exception
}
}
}
data class Foo {}
And I decompile it to Java, in the init block, it calls the getter method of mutableList.
Is there a way to call the mutabbleList itself instead of getter method?
Of course it calls the getter (which returns ImmutableList.copyOf(field)).
You can do simply assignment to mutableList new copied mutable list in your init block:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo>
get() = ImmutableList.copyOf(field)
init {
mutableList = list.map { it.copy() }.toMutableList()
}
}
or whithout init:
data class MutableClass(val list: List<Foo>) {
private val mutableList: MutableList<Foo> = list.map { it.copy() }.toMutableList()
get() = ImmutableList.copyOf(field)
}
Kotlin stdlib opts for interface immutability. That means, the interface an implementation is boxed in determines the mutability of the reference itself.
Therefore, the right way to make a MutableList<T> just a List<T> is to box it, like follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList as List<Int>
That way, being the myImmutableList reference boxed in a List<Int>, it will only expose members from List<Int>, and not those that just MutableList<Int> define, which allow to mutate the state of the object, hence the list.
Then, if you really want to avoid the following issue (resuming from the above code),
val hackedList = myImmutableList as MutableList<Int>
... for which you would be able to access the mutable implementation through unboxing, you may rather be opting for the following solution:
class ImmutableList<T>(list: MutableList<T>) : List<T> by list
fun <T> MutableList<T>.toImmutable() = ImmutableList(this)
And then use it as follows:
val myMutableList = mutableListOf(1, 2, 3, 4)
val myImmutableList = myMutableList.toImmutable()
So you'll be avoiding the issue above. Indeed, any attempt to unbox the value return from MutableList<T>.toImmutable() will end up with a TypeCastException, as the implementation of the List<T> is no longer a MutableList<T>. Rather, it is an ImmutableList<T>, which doesn't expose any methods that might mutate the object.
Unlike #Lucas method, this way you won't be wasting time to copy elements, as you'll be relying on the by keyword in Kotlin, which allows you to implement an interface through an already existing implementation. That is, the MutableList<T> you'll be passing to the constructor of ImmutableList<T>.
When I was researching about this topic, the best solution it just worked for me is just enforcing by contract. If you are creating a mutable list, let's say:
val immutableList = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
).toList() // We make it immutable?
and then you use an extension function or any of the recommendations given below, like using ImmutableList.copyOf(field), you might be paying a penalty because you're copying the items into another collection.
Another option is just paying the unboxing cost of doing something like:
val myImmutableList = myMutableList as List<Int>
The solution I opted for is just enforcing by contract, it's a very simple concept. Your MutableList inherits from List. If you want to share a collection of items with that level of abstraction, it's your choice to do it by enforcing the type:
val immutableList: List<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
now if we share that list with another component, we'll use the right abstraction without any cost. We could also have used a Collection, because List inherits from Collection:
val immutableList: Collection<Randomy> = mutableListOf(
Randomy.One,
Randomy.Two,
Randomy.Three
)
For me, using a var instead of val field along with a private setter usually works best
class Order
class Something() {
var orders: List<Order> = listOf()
private set
fun addOrder(order: Order) {
orders = orders
.toMutableList()
.apply { add(order) }
}
}
This exposes it as immutable and requires a single field only. The price we pay is the overhead of creating a new collection when adding elements it.

How to clone object in Kotlin?

The Kotlin documentation describes cloning only in accessing Java and in enum class. In latter case clone is just throwing an exception.
So, how would I / should I clone arbitrary Kotlin object?
Should I just use clone() as in Java?
For a data class, you can use the compiler-generated copy() method. Note that it will perform a shallow copy.
To create a copy of a collection, use the toList() or toSet() methods, depending on the collection type you need. These methods always create a new copy of a collection; they also perform a shallow copy.
For other classes, there is no Kotlin-specific cloning solution. You can use .clone() if it suits your requirements, or build a different solution if it doesn't.
You can use Gson library to convert the original object to a String and then convert back that String to an actual Object type, and you'll have a clone. Although this is not the intended usage of the Gson library which is actually used to convert between JSON and other object types, but I have devised this method to solve the cloning problem in many of my Kotlin based Android applications.
See my example. Put this function in the class/model of which you want to create a clone. In my example I'm cloning an Animal type object so I'll put it in the Animal class
class Animal{
fun clone(): Animal
{
val stringAnimal = Gson().toJson(this, Animal::class.java)
return Gson().fromJson<Animal>(stringAnimal, Animal::class.java)
}
}
Then use it like this:
val originalAnimal = Animal()
val clonedAnimal = originalAnimal.clone()
A Kotlin data class is easy to clone using .copy()
All values will be shallow copied, be sure to handle any list/array contents carefully.
A useful feature of .copy() is the ability to change any of the values at copy time. With this class:
data class MyData(
val count: Int,
val peanuts: Int?,
val name: String
)
val data = MyData(1, null, "Monkey")
You could set values for any of the properties
val copy = data.copy(peanuts = 100, name = "Elephant")
The result in copy would have values (1, 100, "Elephant")
If the class you are trying to clone does not implement Cloneable or is not a data class and is a part of an outside library, you can create an extension method that returns a new instance. For example:
class Person {
var id: String? = null
var name: String? = null
}
fun Person.clone(): Person {
val person = Person()
person.id = id
person.name = name
return person
}
It requires to implement Cloneable for your class then override clone() as a public like:
public override fun clone(): Any {<your_clone_code>}
https://discuss.kotlinlang.org/t/how-to-use-cloneable/2364/3
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
}
I've voted for #yole for nice answer, but other ways if you don't (or can't) use data class. You can write helper method like this:
object ModelHelper {
inline fun <reified T : Serializable> mergeFields(from: T, to: T) {
from::class.java.declaredFields.forEach { field ->
val isLocked = field.isAccessible
field.isAccessible = true
field.set(to, field.get(from))
field.isAccessible = isLocked
}
}
}
So you can "copy" instance A into B by:
val bInstance = AClassType()
ModelHelper.mergeFields(aInstance, bInstance)
Sometimes, I use this way to merge data from many instances into one object which value available (not null).
Here is a consistent solution that works for any object type:
Kotlin's Array data structure provides a clone() method that can be used to clone the contents of the array:
val a = arrayOf(1)
//Prints one object reference
println(a)
//Prints a different object reference
println(a.clone())
As of Kotlin 1.3, the clone method has been supported on all major targets, so it should be usable across platforms.
It's also possible to clone an object using kotlinx.serialization
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
#Serializable
class A
{
val name: String = "Cloneable class A"
fun clone(): A {
val json = Json(JsonConfiguration.Stable)
val jsonStr = json.stringify(serializer(), this)
return json.parse(serializer(), jsonStr)
}
}
Collection copying functions, such as toList(), toMutableList(), toSet() and others, create a snapshot of a collection at a specific moment. Their result is a new collection of the same elements. If you add or remove elements from the original collection, this won't affect the copies. Copies may be changed independently of the source as well.
val alice = Person("Alice")
val sourceList = mutableListOf(alice, Person("Bob"))
val copyList = sourceList.toList()
sourceList.add(Person("Charles"))
alice.name = "Alicia"
println("First item's name is: ${sourceList[0].name} in source and ${copyList[0].name} in copy")
println("List size is: ${sourceList.size} in source and ${copyList.size} in copy")
First item's name is: Alicia in source and Alicia in copy
List size is: 3 in source and 2 in copy
Kotlin Official Document
Sample Screenshot

is it possible to add a template to the getter/setter of a data class?

for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.