user8608956
user8608956

Reputation:

How to use custom setter in Kotlin class constructor body

I couldn't find a better title for describing how can I avoid code duplication (require expressions) in this Kotlin class:

class Person(email: String) {
    var email: String = email
        set(value) {
            require(value.trim().isNotEmpty(), { "The email cannot be blank" })
            field = value
        }

    init {
        require(email.trim().isNotEmpty(), { "The email cannot be blank" })
    }
}

In java, I would have a setter with the name validation and then I would call it from the constructor.

What is the idiomatic way of doing that in Kotlin ?

Upvotes: 9

Views: 5403

Answers (4)

Qiyu Zhang
Qiyu Zhang

Reputation: 185

There's an alternative way, to use companion object and create object there, it's kind like the constructor and meets your requirement in a different way.

data class Target(
        val date: LocalDate,
        val id: Long,
        val stringId: String,
) {
    companion object {
        fun create(date: LocalDate, id: Long, stringId: String?,): Target{
            val nonNullStringId= if (stringId.isNullOrEmpty()) "" else stringId
            return Target(date, id, nonNullStringId)
        }
    }
}

Upvotes: 0

jah
jah

Reputation: 1305

You can do it just like in java. You just have to remove the primary constructor and make a secondary constructor instead. You will have to do the assignment manually though, like in java. So it would look like this:

class Person {
    constructor(email: String) {
        this.email = email
    }

    var email: String
        set(value) {
            require(value.trim().isNotEmpty(), { "The email cannot be blank" })
            field = value
        }
}

Upvotes: 4

Paul Hicks
Paul Hicks

Reputation: 14029

Define the member outside of the constructor, and invoke the setter from the init block:

class Person(initialEmail: String) { // This is just the constructor parameter.
    var email: String = "" // This is the member declaration which calls the custom setter.
        set(value) {          // This is the custom setter.
            require(value.trim().isNotEmpty(), { "The email cannot be blank" })
            field = value
        }

    init {
        // Set the property at construct time, to invoke the custom setter.
        email = initialEmail
    }
}

Upvotes: 2

Paul Hicks
Paul Hicks

Reputation: 14029

Use a delegate. There's the observable() delegate that already exists.

class Person(initialEmail: String) { // No "var" any more.
    var email: String by Delegates.observable("") {
    _, _, newValue ->
        // This code is called every time the property is set.
        require(newValue.trim().isNotEmpty(), { "The email cannot be blank" })
    }

    init {
        // Set the property at construct time, to invoke the delegate.
        email = initialEmail
    }
}

Upvotes: 3

Related Questions