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
在数据类中,必须使用 val 或 var 标记主构造函数的参数 .
我正在将 _value 的值分配给 value ,以便为属性使用所需的名称 .
我使用您描述的逻辑为属性定义了一个自定义访问器 .
26
答案取决于 data 提供的实际使用的功能 . @EPadron提到了一个漂亮的技巧(改进版):
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
在调用具有错误值的构造函数之前,让创建 data class 的业务逻辑将值更改为0或更大 . This is probably the best approach for most cases.
不要使用 data class . 使用常规 class 并让您的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()
}
}
在对象上创建一个额外的安全属性,它可以执行您想要的操作,而不是具有有效覆盖的私有值 .
data class Test(val value: Int) {
val safeValue: Int
get() = if (value < 0) 0 else value
}
A bad approach that other answers are suggesting:
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
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)
}
}
-1
我发现以下是实现您所需要的最佳方法,而不会破坏 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())
然而,
首先,请注意 _value 是 var ,而不是 val ,但另一方面,因为它很容易确保它在类中没有被修改 .
6 回答
你可以尝试这样的事情:
在数据类中,必须使用
val
或var
标记主构造函数的参数 .我正在将
_value
的值分配给value
,以便为属性使用所需的名称 .我使用您描述的逻辑为属性定义了一个自定义访问器 .
答案取决于
data
提供的实际使用的功能 . @EPadron提到了一个漂亮的技巧(改进版):这将按预期工作,e.i它有 one 字段,一个getter,右
equals
,hashcode
和component1
. 问题是toString
和copy
很奇怪:要解决
toString
的问题,您可以手动重新定义 . 我知道无法修复参数命名但根本不使用data
.在花了差不多整整一年写Kotlin之后,我发现尝试覆盖这样的数据类是一种不好的做法 . 有三种有效的方法,在我提出之后,我将解释为什么其他答案提出的方法很糟糕 .
在调用具有错误值的构造函数之前,让创建
data class
的业务逻辑将值更改为0或更大 . This is probably the best approach for most cases.不要使用
data class
. 使用常规class
并让您的IDE为您生成equals
和hashCode
方法(或者不需要它们) . 是的,如果在对象上更改了任何属性,则必须重新生成它,但是您可以完全控制对象 .A bad approach that other answers are suggesting:
这种方法的问题是data classes并不真正意味着改变这样的数据 . 它们实际上只是用于保存数据 . 覆盖像这样的数据类的getter意味着
Test(0)
和Test(-1)
彼此不会equal
并且会有不同的hashCode
,但是当你调用.value
时,它们会有相同的结果 . 这是不一致的,虽然它可能对你有用,但是你团队中看到这个数据类的其他人可能会意外地误用它而没有意识到你在Map
或Set
中是如何正常工作的 .我知道这是一个老问题,但似乎没有人提到将值设为私有并编写自定义getter的可能性如下:
这应该是完全有效的,因为Kotlin不会为私有字段生成默认的getter .
但是否则我绝对同意数据类用于保存数据的spierce7,你应该避免在那里硬编码“业务”逻辑 .
这似乎是Kotlin令人讨厌的缺点之一(除此之外) .
似乎唯一合理的解决方案,完全保持类的向后兼容性是将其转换为常规类(而不是“数据”类),并手动(借助IDE)实现方法:hashCode( ),equals(),toString(),copy()和componentN()
我发现以下是实现您所需要的最佳方法,而不会破坏
equals
和hashCode
:然而,
首先,请注意
_value
是var
,而不是val
,但另一方面,因为它很容易确保它在类中没有被修改 .其次,
toString()
产生的结果与_value
命名为value
时的结果略有不同,但它是一致的TestData(0).toString() == TestData(-1).toString()
.