0

Say I have a data class

data class MyClass(val crop: Rect, val name: String)

But I want to make a copy of the Rect passed in since I don't want the value to be modified later. I don't want to the caller to call

MyClass(Rect(inCrop), "name")

in the code. How can I do this in my data class?

Thanks.

Robin
  • 10,052
  • 6
  • 31
  • 52

2 Answers2

3

One workaround I can think of is:

data class MyClass(private var privateCrop: Rect, val name: String) {
    val crop get() = privateCrop

    init {
        privateCrop = Rect(privateCrop)
    }
}

You make crop private and make it a var (privateCrop), then you add a public getter for it. Now you can copy it in an init block.

But I gotta admit, this is rather ugly. The better solution here I think is to change Rect to be immutable, but if Rect isn't in your control, then I guess it can't be helped. You might also consider using a regular class.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

You may not want to alter data class's like this. As per another solution's answer, you may find other peculiarities with this solution. The solution given by @Sweeper, also does not include providing a defensive copy, which you may want to do to avoid access to modifying the internal property field.

To quote:

After spending almost a full year of writing Kotlin daily I've found that attempting to override data classes like this is a bad practice. There are 3 valid approaches to this, and after I present them, I'll explain why the approach other answers have suggested is bad.

  1. Have your business logic that creates the data class alter the value to be 0 or greater before calling the constructor with the bad value. This is probably the best approach for most cases.

  2. Don't use a data class. Use a regular class and have your IDE generate the equals and hashCode methods for you (or don't, if you don't need them). Yes, you'll have to re-generate it if any of the properties are changed on the object, but you are left with total control of the object.

   class Test(value: Int) {
     val value: Int = value
       get() = if (field < 0) 0 else field

override fun equals(other: Any?): Boolean {
       if (this === other) return true
       if (other !is Test) return false
       return true
     }

     override fun hashCode(): Int {
       return javaClass.hashCode()
     }
   }
  1. Create an additional safe property on the object that does what you want instead of having a private value that's effectively overriden.
timj11dude
  • 31
  • 5
  • 2
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/31647844) – A S M Sayem May 04 '22 at 19:22