kotlinsetprimary-constructor

Kotlin: How to use custom setters in primary constructor


I don't know how to make it so that when creating an object the values of the parameters "pass through the setters" the closest I've gotten is to duplicate the code, use once in the creation of the object and once again in the setter

class User(var name: String, password: String, age: Int) {

    // IDK how use a custom setter in the primary constructor
    var password: String = if (password.length > 6) password else throw  IllegalArgumentException("Password is too short")
        set(value) {
            if(value.length > 6)
                field = value
            else
                throw IllegalArgumentException("Password is too short")
        }

    var age: Int = if (age > 18) age else throw IllegalArgumentException("Age must be 18+")
        set(value) {
            if(value > 18 )
                field = value
            else
                throw IllegalArgumentException("Age must be 18+")
        }

    override fun toString(): String {
        return "login: $name, password: $password, age: $age"
    }


}

fun main() {

    val user1 = User("User1", "swordfish", 20)
    println(user1)

    try{
        // This code throw a exception
        user1.password = "fish"
    }
    catch (e: IllegalArgumentException){
        println(e.message)
    }

    // Here we can see if password has changed
    println(user1)

    try{
        val user2 = User("U2", "swordfish", 2)
        println(user2)
    }
    catch (e: IllegalArgumentException){
        println(e.message)
    }
}

What I want is to be able to check (through a setter) the parameters that are passed in the creation of an object


Solution

  • There may be many approaches to this but you're not wrong.

    What I'd do to achieve what you're trying to do is use the builtin error() function that throws an IllegalStateException on the JVM.

    class User(var name: String, password: String, age: Int) {
    
    
        var password: String = password
            set(value) {
                assertValidPassword(value)
                field = value
            }
    
        var age: Int = age
           set(value) {
               assertValidAge(age)  
               field = value
           }
    
        init {
             this.password = password
             this.age =age
        }
    
        override fun toString(): String {
            return "login: $name, password: $password, age: $age"
        }
        private fun assertValidAge(age: Int) {
           if (age <=18 ) error("Age must be 18+")
        }
        private fun assertValidPassword(password: String) {
            if(password.length <= 6) error("Password is too short")
        }
    }
    

    What I've done here:

    1. I've put the validation login inside a function that I can reuse both in the setter and the constructor body
    2. I've added the primary constructor body by specifying the init {} block