我在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"
}
答案 0 :(得分:8)
问题:
对于您的第一个Time
课程,测试评估:
val isValid = 0 in 0..59 && 0 in 0..59
因为isValid
将在构造函数执行之前分配,mm
和ss
尚未分配参数,而这些参数会使值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
这种方式将在mm
和ss
被分配后进行评估。如果您的属性在第二个var
类中是可变的(Time
),那么使用getter也会更好。这样,当mm
或ss
更改时,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"
}