英文:
Kotlin Koans Operators Overloading
问题
我用以下代码编写了一个单独的项目,并且感觉程序的表现符合我的预期。
为什么这段代码不能通过 Kotlin Koans 测试?
import TimeInterval.*
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)
// 可以添加到日期的支持的时间间隔:
enum class TimeInterval(var n: Int = 1) {
DAY, WEEK, YEAR
}
operator fun TimeInterval.times(times: Int): TimeInterval {
n *= times
return this
}
operator fun MyDate.plus(timeInterval: TimeInterval): MyDate =
addTimeIntervals(timeInterval, timeInterval.n)
英文:
I wrote a separate project with the following code, and I feel that the program performs as I expected.
Why can't this write through the Kotlin Koans test?
import TimeInterval.*
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)
// Supported intervals that might be added to dates:
enum class TimeInterval(var n: Int = 1) {
DAY, WEEK, YEAR
}
operator fun TimeInterval.times(times: Int): TimeInterval {
n *= times
return this
}
operator fun MyDate.plus(timeInterval: TimeInterval): MyDate =
addTimeIntervals(timeInterval, timeInterval.n)
答案1
得分: 1
这并不是你所想的意思:
enum class TimeInterval(var n: Int = 1) {
DAY, WEEK, YEAR
}
这声明了3个枚举常量 - DAY
、WEEK
和YEAR
。每个常量都有自己的n
属性,初始值为1。你可以这样理解:
data class TimeInterval private constructor(var n: Int = 1) {
companion object {
val DAY = TimeInterval()
val WEEK = TimeInterval()
val YEAR = TimeInterval()
}
}
关键是,只有3个TimeInterval
的实例,没有更多。每次访问DAY
时,并不会得到一个全新的实例。
因此,当你执行n *= times
时,你改变的是TimeInterval
实例的n
值,这个改变会在下次使用该实例时保持。例如:
val fiveDays = DAY * 5 // 这将DAY.n设置为1 * 5 = 5
val twoDays = DAY * 2 // 这将DAY.n设置为5 * 2 = 10
// 这两个都会打印出10!
println(fiveDays.n)
println(twoDays.n)
因此,在枚举类的构造函数中使用var
或任何可变的东西可能会导致一些非常令人困惑的行为,几乎永远不是你想要的。
当然,如果你不多次使用TimeInterval
的任何实例,你可能不会注意到这一点。但是Koan上的测试确实多次使用它们。
你应该使用一种组合TimeInterval
和一个数字的方法。你可以使用Pair<TimeInterval, Int>
,或者更好的是,按照Koan中的提示编写一个新类。
英文:
This does not mean what you think it does:
enum class TimeInterval(var n: Int = 1) {
DAY, WEEK, YEAR
}
This declares 3 enum constants - DAY
, WEEK
and YEAR
. Each constant has its own n
property, initially set to 1. You can think of this as:
data class TimeInterval private constructor(var n: Int = 1) {
companion object {
val DAY = TimeInterval()
val WEEK = TimeInterval()
val YEAR = TimeInterval()
}
}
Crucially, there are only 3 instances of TimeInterval
, period. You don't get a brand new instance every time you access DAY
for example.
Therefore, when you do n *= times
, you are changing the value of n
for that instance of TimeInterval
, and this change persists the next time you use that instance. e.g.
val fiveDays = DAY * 5 // this sets DAY.n to 1 * 5 = 5
val twoDays = DAY * 2 // this sets DAY.n to 5 * 2 = 10
// both of these will print 10!
println(fiveDays.n)
println(twoDays.n)
Therefore, using var
s, or anything mutable in enum class' constructors can cause some very confusing behaviours, and is almost never what you want.
Of course, you don't notice this if you don't use any instance of TimeInterval
more than once. But the tests on the Koan do use them more than once.
You should use something that composes a TimeInterval
and a number. You can use a Pair<TimeInterval, Int>
, or better, write a new class, as the hint in the Koan says.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论