如何在 Kotlin 中实现验证构造函数?

sir*_*sel 4 validation constructor builder kotlin

我正在 Kotlin 中实现 vars,它可以接收来自某些用户的越界输入。我希望能够调用可选(只从这些非信任用户输入)进行验证的构造函数可以返回空值,然后使用Elvis操作符无效输入的情况下指定默认值,如: var myFoo = Foo.fromInt(i) ?: 1。我目前正在使用伴随对象方法来验证输入:

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
}
Run Code Online (Sandbox Code Playgroud)

当我子类化时Foo,我知道我必须重新创建伴随对象。我试过从抽象类继承同伴;然而,调用Foo(x)仍然指向Foo而不是Bar,除非我fromInt在每个子类的同伴中覆盖。是否有更好或更符合 Kotlin 习惯的方法来处理这种可以返回null而不是创建请求对象的验证构造函数模式?

sma*_*c89 8

您可以使用 init 块以及require.

open class Foo(val x:Int) {
  init {
    require(x in 1..3) {
      "relies on valid x in 1..3"
    }
  }
}
class Bar(x:Int) : Foo(x)
Run Code Online (Sandbox Code Playgroud)

示例运行:

>>> open class Foo(val x:Int) {
...   init {
...     require(x in 1..3) {
...       "relies on valid x in 1..3"
...     }
...   }
... }
>>> class Bar(x:Int) : Foo(x)
>>> val b = Bar(3)
>>> val b = Bar(4)
java.lang.IllegalArgumentException: relies on valid x in 1..3
    at Line_18$Foo.<init>(Line_18.kts:3)
    at Line_19$Bar.<init>(Line_19.kts:1)
>>> b.x
res22: kotlin.Int = 3
Run Code Online (Sandbox Code Playgroud)


s1m*_*nw1 6

您是否可以简单地使用单个构造函数并始终验证其参数x并将其分配给如下所示的属性:

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) 
Run Code Online (Sandbox Code Playgroud)

现在使用不在范围内的值调用构造函数,Foo创建了一个默认值,这基本上是我认为你想要的。在我看来,如果你抛出一个IllegalArgumentException而不是在幕后创建默认值会更好。你怎么认为?


And*_*d14 5

您还可以查找“重载 invoke() 函数以进行构造函数验证”

class Student private constructor(val rollNo: Int) {
companion object {
    operator fun invoke(rollNo: Int): Student {
        //Do your validation here
        if (rollNo < 1) {
            throw Exception() // or any default Student
        } else
            return Student(rollNo)
    }
}
Run Code Online (Sandbox Code Playgroud)

}

val student1 =Student(4)
val student2 =Student(-1) // will throw exception
Run Code Online (Sandbox Code Playgroud)

这种方法隐藏了构造函数(私有),并且由于重载的语法,看起来我们正在进行构造函数调用。