Search code examples
validationconstructorkotlinbuilder

How to implement a validating constructor in Kotlin?


I'm implementing vars in Kotlin that could receive out-of-bounds input from certain of their users. I'd like to be able to optionally call (only on input from these untrusted users) a validating constructor that can return null, and then use the Elvis operator to specify defaults in the case of invalid input, as in: var myFoo = Foo.fromInt(i) ?: 1. I'm currently using a companion object method to validate the input:

open class Foo(val x:Int) {   // relies on valid x in 1..3
  companion object {
    fun fromInt(x: Int): Foo? = if (x in 1..3) Foo(x) else null
  }
}
class Bar(x:Int) : Foo(x)

fun main(args:Array<String>) {
  println(Foo.fromInt(2))    // Foo(2)
  println(Foo.fromInt(20))   // null
  // println(Bar.fromInt(2))
  // would like Bar(2) but as expected, companion isn't inherited
}

When I subclass Foo, I know I have to recreate the companion object. I've tried inheriting the companion from an abstract class; however, the call to Foo(x) still points to Foo and not Bar, unless I override fromInt in each subclass' companion. Is there a better or more Kotlin-idomatic way to deal with this kind of pattern of a validating constructor that can return null rather than creating the requested object?


Solution

  • could you simply use a single constructor and always validate its argument x and assign it to a property like here:

    open class Foo (x: Int) {   // relies on valid x in 1..3
        val x: Int = if (x in 1..3) x else 1
    }
    
    class Bar(x: Int) : Foo(x) 
    

    Now calling the constructor with a value that is not in range, a default Foo is created, which is basically what you wanted to have I think. In my opinion it would be better if you threw a IllegalArgumentException instead of creating a default behind the scenes. What do you think?