When creating a data class I frequently find that I want to transform one of the properties, usually to normalize it or to make a defensive copy. For example, here I want productCode
to always be lowercase:
data class Product(val productCode: String)
I've tried adding an init
block, in the hopes that Kotlin would be smart enough to let me manually deal with the assignment of the constructor parameter to the property:
data class Product(val productCode: String) {
init {
this.productCode = productCode.toLowerCase()
}
}
but it treats this as a reassignment.
I'd rather not have to write equals
/hashCode
/toString
/copy
by hand and IDE generated methods aren't really much better.
Is there any way to transform constructor parameters in a data class?
No. For equality and toString to work, the properties need to be in the primary constructor.
What you can do however, is create a factory method:
data class Product private constructor(val productCode: String) {
companion object Factory {
fun create(productCode: String) : Product {
return Product(productCode.toLowerCase())
}
}
}
By making the constructor private
you force usage of this create
method.
If you want to get 'hacky', you can pretend you're still calling the constructor, by renaming create
to invoke
and making it an operator
function:
data class Product private constructor(val productCode: String) {
companion object {
operator fun invoke(productCode: String): Product {
return Product(productCode.toLowerCase())
}
}
}
Calling Product("foo")
will call the invoke
method.
Note: the constructor is still exposed through the copy
method, see https://youtrack.jetbrains.com/issue/KT-11914