Kotlin - 错误的val导致带有辅助构造函数的类

时间:2018-02-17 12:50:56

标签: kotlin

我在Kotlin有以下课程:

open class Time {

    var hh: Int = 0
    var mm: Int = 0
    var ss: Int = 0

    constructor(hh: Int, mm: Int, ss: Int) {
        this.hh = hh
        this.mm = mm
        this.ss = ss
    }

    constructor(seconds: Int) {
        this.hh = seconds / 3600
        this.mm = (seconds % 3600) / 60
        this.ss = (seconds % 3600) % 60
    }

    val isValid = mm in 0..59 && ss in 0..59

    val toSeconds = hh * 3600 + mm * 60 + ss

    val toString = "$hh:$mm:$ss"

}

当我运行下一个单元测试时,它失败了:

assertFalse(Time(0, 59, 60).isValid)

但是当我改为主构造函数时,测试会返回预期的结果:

open class Time(val hh: Int, val mm: Int, val ss: Int) {

    constructor(seconds: Int) : this(seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60)

    val isValid = mm in 0..59 && ss in 0..59

    val toSeconds = hh * 3600 + mm * 60 + ss

    val toString = "$hh:$mm:$ss"
} 

1 个答案:

答案 0 :(得分:8)

问题:

对于您的第一个Time课程,测试评估:

val isValid = 0 in 0..59 && 0 in 0..59

因为isValid将在构造函数执行之前分配,mmss尚未分配参数,而这些参数会使值0。< / p>

Time类的第二个版本中,构造函数首先运行,然后运行

val isValid = 59 in 0..59 && 60 in 0..59

isValid将分配您传递的值。

<强>建议:

您的第二个版本就是您在Kotlin中编写有关构造函数的方式。但是如果你想让第一个版本也可以工作,可以将isValid改为没有支持字段的属性。

val isValid get() = mm in 0..59 && ss in 0..59

这种方式将在mmss被分配后进行评估。如果您的属性在第二个var类中是可变的(Time),那么使用getter也会更好。这样,当mmss更改时,isValid将使用新值重新进行重新评估。这同样适用于toSeconds属性。

toString也应该有不同的定义:

override fun toString() = "$hh:$mm:$ss"

因为这是其他类所期望的约定,它可以实现类似:

println(Time(0, 59, 60).toStrting()) // explicit call
println(Time(0, 59, 60)) // toString() will be invoked implicitely

最后,你的课应该是这样的:

open class Time(var hh: Int, var mm: Int, var ss: Int) {

    constructor(seconds: Int) : this(
       seconds / 3600, 
       (seconds % 3600) / 60, 
       (seconds % 3600) % 60
    )

    val isValid get() = mm in 0..59 && ss in 0..59
    val toSeconds get() = hh * 3600 + mm * 60 + ss
    override fun toString() = "$hh:$mm:$ss"
}