重写Kotlin数据类的getter
考虑到以下Kotlin类:重写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
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)
}
}