Kotlin Koans 运算符重载

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

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-2.html
匿名

发表评论

匿名网友

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

确定