Kaourintin Tamine
Kaourintin Tamine

Reputation: 33

Kotlin : implenting an immutable class through the data class method but making sure the input values are clean

I'm new to coding in kotlin and want to implement an immutable class that represents a project with various fields inside.

The easiest way to do this is by using a data class and using the copy() method so that anytime one of the app user modifies a field it results in the backend in a call to the copy method with the modified field producing the new project.

My problem is that this way does not allow for prior checking of parameters (eg : limit string size of the owner, making sure the number of people added to the project is reasonable etc). If this was java, I'd use a builder pattern but this seems to defeat the purpose of kotlin, and i've read articles that are positive to using builders in kotlin (https://www.baeldung.com/kotlin/builder-pattern) and others that are completely against (https://code-held.com/2021/01/23/dont-use-builder-in-kotlin/).

I haven't found any way to "modify" the copy method and to add the parameter sanitization checks that are needed for each parameter. I would appreciate any "smooth" idea to implement this, if anybody has found it. The goal would also be to throw exeptions/sealed classes variables so that the app UI can tell the user what went wrong instead of a generic error message just mentioning that the project was not modified.

Upvotes: 3

Views: 801

Answers (1)

Tenfour04
Tenfour04

Reputation: 93739

I agree with the second link. If you look at the comments on the Baeldung article, you'll see even they were convinced and pledged to revise the article.

You can throw exceptions in an init block but if these are exceptions that are not caused by programmer error, it would be more Kotlin-idiomatic to expose a single constructor-like function that returns a wrapper or just null for invalid input.

Examples:

data class Person(val name: String, val age: Int = 0) {
    init {
        if (age < 0) {
            throw IllegalArgumentException("Age $age is less than 0.")
        }
    }
}

If you want to return a wrapper or nullable, a data class isn't suitable for preventing invalid input because the generated copy() function will always return a fully constructed object. Sadly, Kotlin does not support overriding the generated copy() function.

sealed class Result<T>
data class Success<T>(val value: T): Result<T>()
data class Failure<T>(val reason: String): Result<T>()

class Person private constructor(val name: String, val age: Int = 0) {
    companion object {
        fun build(name: String, age: Int = 0): Result<Person> {
            return when {
                age < 0 -> Failure("Age $age is less than 0.")
                else -> Success(Person(name, age))
            }
        }
    }
    
    fun buildCopy(name: String = this.name, age: Int = this.age) = build(name, age)
}

Upvotes: 1

Related Questions