1

I have a data class like this

data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(),
              .....
              val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()) 

I have two copies of the object:

person = originalPerson.copy()

Then I change the elements of personConsents for the object person - I add/delete/edit them. But by some reason I see that the same changes are happening in originalPerson object which I don't want to be. originalPerson is not supposed to be changed at all. Suspect there is something with ArrayList references, but need your advice what i can do? At the end I need to compare two objects likefun dataChanged(): Boolean = originalPerson != person bu it doesn't work when ArrayList is changing.

Andrey_yog
  • 310
  • 2
  • 11
  • 1
    Possible duplicate of [Kotlin data class copy method not deep copying all members](https://stackoverflow.com/questions/47359496/kotlin-data-class-copy-method-not-deep-copying-all-members) – Satej S Feb 08 '18 at 09:07

3 Answers3

2

I found a simple solution. I use my own clone function which creates a new object for ArrayList and fill it by copied elements.

fun getPersonClone(person: Person): Person {
    val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()
    person.personConsents.forEach { personConsents.add(it.copy()) }
    return Person(person.id, ......., personConsents)
}
Andrey_yog
  • 310
  • 2
  • 11
1

So,this link here, will help you understand that the copy method in Kotlin, does not do a deep copy, it only does a shallow one. This is specially seen with non-primitive data types such as the ArrayList one you're using.

If you must use a method to copy the data class directly, what you can do is serialize it, and then de-serialize it.

I've done something like this below, using Gson.

Using the Data Class

 data class Person(var name: String? = null,val cars : ArrayList<String> = ArrayList() )

The main method

fun main (args: Array<String>) {

    var original =Person("Jon", arrayListOf("Honda City","VW Vento"))
    var clone =Gson().fromJson(Gson().toJson(original), Person::class.java)
    original.cars.add("VW Polo")
    original.name = "Henry"
    print(clone.cars) // Prints [Honda City, VW Vento]   

}

This approach seems really hacky, and I'd encourage anything better.

Satej S
  • 2,113
  • 1
  • 16
  • 22
  • Thanks for the answer but don't want to do such as hacky triks. Will try to find a better solution. At least I can create a separate ArrayList object to compare the elements with the elements of the person.ArrayList – Andrey_yog Feb 08 '18 at 09:43
0

The Kotlin copy method is a shallow copy. So your copy ends up referencing the exact same array as the original object.

I would say the simplest way to fix this is to implement the copy method yourself (no need to create an extra custom clone method):

data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(), val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()) {
    fun copy() = Person(this.id, ArrayList(this.personConsents))
}
Ekeko
  • 1,879
  • 14
  • 15