kotlindata-class

Is there any way to transform the value of a property at data class construction time?


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?


Solution

  • 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