覆盖Kotlin数据类的getter

鉴于以下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 
  • 在一个数据类中,你必须使用valvar来标记主构造函数的参数。

  • 我将_value的值_valuevalue ,以便为属性使用所需的名称。

  • 我用你描述的逻辑定义了属性的自定义访问器。

在花了差不多一年的时间写完Kotlin后,我发现用数据类来做这件事是一个不好的做法。 他们是这三个有效的方法,之后我会解释为什么其他人提出的方法是不好的。

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

  2. 不要使用data class 。 使用一个普通的class ,让你的IDE为你生成equalshashCode方法(或者不要,如果你不需要的话)。 是的,如果对象上的任何属性发生了更改,则必须重新生成该对象,但是只能完全控制该对象。

     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() } } 
  3. 创建一个额外的安全属性的对象,做你想要的,而不是有效的覆盖私有价值。

     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 s,但是当你调用.value ,他们会有相同的结果。 这是不一致的,虽然它可能对你有帮助,但你的团队中的其他人看到这是一个数据类,可能会意外地误用它,却没有意识到你是如何改变它/使其不能按预期工作(即,在MapSet正常工作)。

答案取决于您实际使用的data提供的function。 @EPadron提到了一个漂亮的技巧(改进版本):

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

这将按预期工作,即它有一个字段,一个getter,right equalshashcodecomponent1 。 赶上是toStringcopy是奇怪的:

 println(Test(1)) // prints: Test(_value=1) Test(1).copy(_value = 5) // <- weird naming 

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

你可以简单

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

请参阅属性和字段

这似乎是一个(除其他之外)Kotlin恼人的缺点。

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

 class Data3(i: Int) { var i: Int = i override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false other as Data3 if (i != other.i) return false return true } override fun hashCode(): Int { return i } override fun toString(): String { return "Data3(i=$i)" } fun component1():Int = i fun copy(i: Int = this.i): Data3 { return Data3(i) } } 

我知道这是一个古老的问题,但似乎没有人提到这样做的价值私人和写自定义getter的可能性:

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

这应该是完全有效的,因为Kotlin不会为私人领域产生默认的获取者。

但除此之外,我绝对同意spierce7数据类是用于保存数据,你应该避免在那里硬编码“业务”逻辑。