重写Kotlin数据类的getter

重写Kotlin数据类的getter,kotlin,Kotlin,考虑到以下Kotlin类: data class Test(val value: Int) 如何重写Intgetter,使其在值为负值时返回0 如果这是不可能的,有哪些技巧可以达到合适的效果?您可以尝试以下方法: data class Test(private val _value: Int) { val value = _value get(): Int { return if (field < 0) 0 else field } } assert(1

考虑到以下Kotlin类:

data class Test(val value: Int)
如何重写
Int
getter,使其在值为负值时返回0


如果这是不可能的,有哪些技巧可以达到合适的效果?

您可以尝试以下方法:

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

assert(1 == Test(1).value)
assert(0 == Test(0).value)
assert(0 == Test(-1).value)

assert(1 == Test(1)._value) // Fail because _value is private
assert(0 == Test(0)._value) // Fail because _value is private
assert(0 == Test(-1)._value) // Fail because _value is private
数据类测试(私有值:Int){
val值=_值
get():Int{
返回if(字段<0)0 else字段
}
}
断言(1==测试(1).value)
断言(0==测试(0).value)
断言(0==测试(-1).value)
断言(1==测试(1)。\u值)//失败,因为\u值是私有的
断言(0==测试(0)。\u值)//失败,因为\u值是私有的
断言(0==测试(-1)。\u值)//失败,因为\u值是私有的
  • 在数据类中,必须使用
    val
    var
    标记主构造函数的参数

  • 我将
    \u value
    的值指定给
    value
    ,以便为属性使用所需的名称

  • 我使用您描述的逻辑为属性定义了一个自定义访问器


    • 答案取决于
      数据提供的实际使用功能@EPadron提到了一个漂亮的技巧(改进版):

      要解决
      toString
      的问题,您可以手动重新定义它。我知道没有办法修复参数命名,但根本不使用
      数据。

      这似乎是Kotlin的一个(以及其他)恼人的缺点

      看起来,唯一能完全保持类向后兼容性的合理解决方案是将其转换为常规类(而不是“数据”类),并手动实现(借助IDE)方法:hashCode()、equals()、toString()、copy()和componentN()


      在花了将近一年的时间编写Kotlin daily之后,我发现像这样试图重写数据类是一种糟糕的做法。有三种有效的方法,在我介绍它们之后,我将解释为什么其他答案所建议的方法是不好的

    • 在使用错误值调用构造函数之前,让创建
      数据类的业务逻辑将值更改为0或更大这可能是大多数情况下的最佳方法。

    • 不要使用
      数据类
      。使用常规的
      ,让IDE为您生成
      equals
      hashCode
      方法(如果不需要,也可以不生成)。是的,如果对象上的任何属性发生更改,则必须重新生成该属性,但您仍可以完全控制该对象

      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()
        }
      }
      
      类测试(值:Int){
      val值:Int=value
      get()=if(field<0)0 else字段
      覆盖乐趣等于(其他:任何?):布尔值{
      如果(this==other)返回true
      如果(其他!是测试)返回false
      返回真值
      }
      重写哈希代码():Int{
      返回javaClass.hashCode()
      }
      }
      
    • 在对象上创建一个附加的安全属性,该属性执行您想要的操作,而不是有效重写私有值

      data class Test(val value: Int) {
        val safeValue: Int
          get() = if (value < 0) 0 else value
      }
      
      数据类测试(val值:Int){
      val安全值:Int
      get()=如果(值<0)0其他值
      }
      
    • 其他答案所建议的糟糕方法:

      data class Test(private val _value: Int) {
        val value: Int
          get() = if (_value < 0) 0 else _value
      }
      
      数据类测试(私有值:Int){
      val值:Int
      get()=如果(_值<0)0其他_值
      }
      

      这种方法的问题在于,它们并不真正用于像这样修改数据。它们实际上只是用来保存数据的。为这样的数据类重写getter意味着
      Test(0)
      Test(-1)
      不会
      相等,并且会有不同的
      hashCode
      s,但是当调用
      .value
      时,它们会有相同的结果。这是不一致的,虽然它可能对您有效,但您团队中看到这是一个数据类的其他人可能会意外地滥用它,而没有意识到您是如何更改它/使它无法按预期工作的(即,这种方法在
      映射
      中无法正确工作).

      我知道这是一个老问题,但似乎没有人提到将值私有化并编写自定义getter的可能性:

      data class Test(private val value: Int) {
          fun getValue(): Int = if (value < 0) 0 else value
      }
      
      数据类测试(私有值:Int){
      fun getValue():Int=if(value<0)0 else值
      }
      
      这应该是完全有效的,因为Kotlin不会为私有字段生成默认getter


      但除此之外,我绝对同意spierce7的观点,即数据类用于保存数据,您应该避免硬编码“业务”逻辑。

      我发现以下是在不破坏
      equals
      hashCode
      的情况下实现所需的最佳方法:

      data class TestData(private var _value: Int) {
          init {
              _value = if (_value < 0) 0 else _value
          }
      
          val value: Int
              get() = _value
      }
      
      // Test value
      assert(1 == TestData(1).value)
      assert(0 == TestData(-1).value)
      assert(0 == TestData(0).value)
      
      // Test copy()
      assert(0 == TestData(-1).copy().value)
      assert(0 == TestData(1).copy(-1).value)
      assert(1 == TestData(-1).copy(1).value)
      
      // Test toString()
      assert("TestData(_value=1)" == TestData(1).toString())
      assert("TestData(_value=0)" == TestData(-1).toString())
      assert("TestData(_value=0)" == TestData(0).toString())
      assert(TestData(0).toString() == TestData(-1).toString())
      
      // Test equals
      assert(TestData(0) == TestData(-1))
      assert(TestData(0) == TestData(-1).copy())
      assert(TestData(0) == TestData(1).copy(-1))
      assert(TestData(1) == TestData(-1).copy(1))
      
      // Test hashCode()
      assert(TestData(0).hashCode() == TestData(-1).hashCode())
      assert(TestData(1).hashCode() != TestData(-1).hashCode())
      
      数据类TestData(私有变量值:Int){
      初始化{
      _值=如果(_值<0)0其他值_值
      }
      val值:Int
      get()=\u值
      }
      //测试值
      断言(1==TestData(1.value)
      断言(0==TestData(-1).value)
      断言(0==TestData(0.value)
      //测试副本()
      断言(0==TestData(-1).copy().value)
      断言(0==TestData(1).copy(-1).value)
      断言(1==TestData(-1).copy(1).value)
      //测试toString()
      assert(“TestData(_value=1)”==TestData(1.toString())
      assert(“TestData(_value=0)”==TestData(-1).toString()
      assert(“TestData(_value=0)”==TestData(0.toString())
      断言(TestData(0).toString()==TestData(-1).toString())
      //测试等于
      断言(TestData(0)=TestData(-1))
      断言(TestData(0)=TestData(-1).copy()
      断言(TestData(0)=TestData(1.copy(-1))
      断言(TestData(1)=TestData(-1).copy(1))
      //测试hashCode()
      断言(TestData(0).hashCode()==TestData(-1).hashCode())
      断言(TestData(1).hashCode()!=TestData(-1).hashCode())
      
      但是,

      首先,请注意,
      \u value
      var
      ,而不是
      val
      ,但另一方面,由于它是私有的,并且不能从中继承数据类,因此很容易确保它不会在类中被修改

      第二
      data class Test(private val _value: Int) {
        val value: Int
          get() = if (_value < 0) 0 else _value
      }
      
      data class Test(private val value: Int) {
          fun getValue(): Int = if (value < 0) 0 else value
      }
      
      data class TestData(private var _value: Int) {
          init {
              _value = if (_value < 0) 0 else _value
          }
      
          val value: Int
              get() = _value
      }
      
      // Test value
      assert(1 == TestData(1).value)
      assert(0 == TestData(-1).value)
      assert(0 == TestData(0).value)
      
      // Test copy()
      assert(0 == TestData(-1).copy().value)
      assert(0 == TestData(1).copy(-1).value)
      assert(1 == TestData(-1).copy(1).value)
      
      // Test toString()
      assert("TestData(_value=1)" == TestData(1).toString())
      assert("TestData(_value=0)" == TestData(-1).toString())
      assert("TestData(_value=0)" == TestData(0).toString())
      assert(TestData(0).toString() == TestData(-1).toString())
      
      // Test equals
      assert(TestData(0) == TestData(-1))
      assert(TestData(0) == TestData(-1).copy())
      assert(TestData(0) == TestData(1).copy(-1))
      assert(TestData(1) == TestData(-1).copy(1))
      
      // Test hashCode()
      assert(TestData(0).hashCode() == TestData(-1).hashCode())
      assert(TestData(1).hashCode() != TestData(-1).hashCode())
      
      data class Recording(
          val id: Int = 0,
          val createdAt: Date = Date(),
          val path: String,
          val deleted: Boolean = false,
          var fileName: String = "",
          val duration: Int = 0,
          var format: String = " "
      ) {
          init {
              if (fileName.isEmpty())
                  fileName = path.substring(path.lastIndexOf('\\'))
      
              if (format.isEmpty())
                  format = path.substring(path.lastIndexOf('.'))
      
          }
      
      
          fun asEntity(): rc {
              return rc(id, createdAt, path, deleted, fileName, duration, format)
          }
      }