覆盖Kotlin数据类的getter

问题描述:

给出以下Kotlin类:

Given the following Kotlin class:

data class Test(val value: Int)

我该如何覆盖Int吸气剂,以使它在值为负数时返回0?

How would I override the Int getter so that it returns 0 if the value negative?

如果这不可能,那么有什么技术可以达到合适的结果?

If this isn't possible, what are some techniques to achieve a suitable result?

每天花费近整整一年的时间写Kotlin之后,我发现尝试像这样重写数据类是一种不好的做法.有3种有效的方法,在介绍它们之后,我将解释为什么其他答案建议的方法不好.

After spending almost a full year of writing Kotlin daily I've found that attempting to override data classes like this is a bad practice. There are 3 valid approaches to this, and after I present them, I'll explain why the approach other answers have suggested is bad.

  1. 在创建具有错误值的构造函数之前,让您的业务逻辑创建data class将该值更改为0或更大. 这可能是大多数情况下的最佳方法.

  1. Have your business logic that creates the data class alter the value to be 0 or greater before calling the constructor with the bad value. This is probably the best approach for most cases.

请勿使用data class.使用常规的class并让您的IDE为您生成equalshashCode方法(或者,如果不需要它们,则不生成).是的,如果在对象上更改了任何属性,则必须重新生成它,但是您完全可以控制该对象.

Don't use a data class. Use a regular class and have your IDE generate the equals and hashCode methods for you (or don't, if you don't need them). Yes, you'll have to re-generate it if any of the properties are changed on the object, but you are left with total control of the object.

class Test(value: Int) {
  val value: Int = value
    get() = if (field < 0) 0 else field

  override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (other !is Test) return false
    return true
  }

  override fun hashCode(): Int {
    return javaClass.hashCode()
  }
}

  • 在对象上创建一个附加的安全属性,该属性执行您想要的操作,而不是具有有效覆盖的私有值.

  • Create an additional safe property on the object that does what you want instead of having a private value that's effectively overriden.

    data class Test(val value: Int) {
      val safeValue: Int
        get() = if (value < 0) 0 else value
    }
    

  • 其他答案提示的一种不好的方法:

    data class Test(private val _value: Int) {
      val value: Int
        get() = if (_value < 0) 0 else _value
    }
    

    这种方法的问题在于,数据类并不是真正的意思.用于更改这样的数据.它们实际上只是用于保存数据.覆盖这样的数据类的getter意味着Test(0)Test(-1)不会相互equal并具有不同的hashCode,但是当您调用.value时,它们将具有相同的结果.这是不一致的,虽然它可能对您有用,但是团队中其他人看到这是数据类时,可能会不小心滥用了它,而没有意识到您如何更改它/使它无法按预期工作(即,这种方法不会不能在MapSet中正常工作.)

    The problem with this approach is that data classes aren't really meant for altering data like this. They are really just for holding data. Overriding the getter for a data class like this would mean that Test(0) and Test(-1) wouldn't equal one another and would have different hashCodes, but when you called .value, they would have the same result. This is inconsistent, and while it may work for you, other people on your team who see this is a data class, may accidentally misuse it without realizing how you've altered it / made it not work as expected (i.e. this approach wouldn't work correctly in a Map or a Set).