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.
-
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. -
Don’t use a
data class
. Use a regularclass
and have your IDE generate theequals
andhashCode
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() } }
-
Create an additional safe property on the object that does what you want instead of having a private value that’s effectively overriden.
data class Test(val value: Int) { val safeValue: Int get() = if (value < 0) 0 else value }
A bad approach that other answers are suggesting:
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
The problem with this approach is that data classes aren’t really meant for altering data like this. They are really just for holding data. Overriding the getter for a data class like this would mean that Test(0)
and Test(-1)
wouldn’t equal
one another and would have different hashCode
s, but when you called .value
, they would have the same result. This is inconsistent, and while it may work for you, other people on your team who see this is a data class, may accidentally misuse it without realizing how you’ve altered it / made it not work as expected (i.e. this approach wouldn’t work correctly in a Map
or a Set
).