给定以下Kotlin类:
data class Test(val value: Int)
Int如果值负,我将如何覆盖吸气剂,使其返回0?
如果这不可能,那么有什么技术可以达到合适的结果呢?
Answers:
在每天花费近整整一年的时间写Kotlin之后,我发现尝试像这样重写数据类是一个不好的做法。有3种有效的方法,在介绍它们之后,我将解释为什么其他答案建议的方法不好。
data class在调用具有错误值的构造函数之前,让您的业务逻辑将alter值创建为0或更大。对于大多数情况,这可能是最好的方法。
不要使用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
}
其他答案提示的一种不好的方法:
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
这种方法的问题在于,数据类并不是真的要像这样更改数据。它们实际上只是用于保存数据。重写像这样的数据类,吸气将意味着Test(0)并Test(-1)不会equal彼此会有不同的hashCodeS,但是当你打电话.value,他们将有相同的结果。这是不一致的,虽然它可能对您有用,但是团队中其他人看到这是一个数据类时,可能会无意间滥用了它,而没有意识到您如何更改它/使其无法按预期工作(即,这种方法不会不能在a Map或Set)中正常工作。
data class class(@JsonProperty("iss_position") private val position: Map<String, Double>) { val latitude = position["latitude"]; val longitude = position["longitude"] },我认为这对我的情况相当不错,tbh。你怎么看待这件事?(还有off其他字段,因此我认为在我的代码中重新创建嵌套的json结构对我来说是没有意义的)
parsing a string into an int,您显然允许将解析和错误处理非数字字符串的业务逻辑引入模型类中……
List,也没有MutableList任何理由。
您可以尝试这样的事情:
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。
我正在分配_valueto 的值,value以便为属性使用所需的名称。
我使用您描述的逻辑为属性定义了自定义访问器。
答案取决于您实际使用的功能data。@EPadron提到了一个漂亮的技巧(改进版):
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
这将正常工作,EI它有一个领域,一个消气吧equals,hashcode和component1。美中不足的是,toString和copy怪异:
println(Test(1)) // prints: Test(_value=1)
Test(1).copy(_value = 5) // <- weird naming
要解决该问题,toString可以手动重新定义。我知道没有办法修复参数命名,但根本不使用data。
我知道这是一个老问题,但似乎没有人提到将价值私有化并编写自定义getter的可能性:
data class Test(private val value: Int) {
fun getValue(): Int = if (value < 0) 0 else value
}
这应该是完全有效的,因为Kotlin不会为私有字段生成默认的getter。
但除此之外,我绝对同意spierce7的观点,即数据类用于保存数据,因此您应避免在其中硬编码“业务”逻辑。
val value = test.getValue() 而不是像其他获取方法 那样来称呼它 val value = test.value
.getValue()
我已经看到了您的答案,我同意数据类仅用于保存数据,但是有时我们需要从中获取一些东西。
这是我对数据类所做的事情,我将一些属性从val更改为var,并在构造函数中覆盖了它们。
像这样:
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)
}
}
fun Recording(...): Recording { ... })。另外,也许数据类不是您想要的,因为使用非数据类可以将属性与构造函数参数分开。最好在类定义中明确您的可变性意图。如果这些字段也无论如何都是可变的,那么数据类就可以了,但是我几乎所有的数据类都是不可变的。
这似乎是Kotlin令人讨厌的缺点之一。
看来,完全保持该类的向后兼容性的唯一合理的解决方案是将其转换为常规类(而不是“数据”类),并手动实现(借助IDE)以下方法: ),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)
}
}
我发现以下是实现您所需要的而不中断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,但是另一方面,由于它是私有的并且不能继承数据类,因此很容易确保不在类中对其进行修改。
其次,toString()产生的结果与如果_value命名为会略有不同value,但是与和一致TestData(0).toString() == TestData(-1).toString()。
_value在init块中被修改,equals并且hashCode 没有损坏。