Robin
Robin

Reputation: 10368

How to modify the parameter pass to kotlin data class?

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.

Upvotes: 0

Views: 2077

Answers (2)

timj11dude
timj11dude

Reputation: 51

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.

Upvotes: 0

Sweeper
Sweeper

Reputation: 271625

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.

Upvotes: 3

Related Questions