Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 379
Laurence Gonsalves Avatar asked Mar 29 '18 17:03

Laurence Gonsalves


People also ask

Can data class have secondary constructor?

A Kotlin data class must have a primary constructor that defines at least one member. Other than that, you can add secondary constructors as explained in Classes and Inheritance - Secondary Constructors.

Can data classes have methods Kotlin?

Constructor Kotlin data class. The compiler generates the utility methods based on the declaration of the primary constructor. For the data class to work as expected, your data classes have to fulfill the below requirements: The primary constructor with at least one parameter.

When should I use Kotlin data class?

Answer: Kotlin provides a special type of class called data class, which is usually used for objects that act as a store for data properties and has no business logic or member functions. It provides a lot of advantages with reduced boilerplate code.

How do you override getter Kotlin?

You can't override getter in kotlin data class with error of Data class primary constructor must have only property (val/ var) parameters . While you can do so in a regular class.


Video Answer


1 Answers

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

like image 172
nhaarman Avatar answered Oct 09 '22 07:10

nhaarman