Kotlin Koans 运算符重载

huangapple go评论52阅读模式
英文:

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个枚举常量 - DAYWEEKYEAR。每个常量都有自己的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 vars, 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&lt;TimeInterval, Int&gt;, or better, write a new class, as the hint in the Koan says.

huangapple
  • 本文由 发表于 2023年8月9日 09:39:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864052.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定