可重用的解决方案:为了有一种简单的方法来选择要包含在equals()
和中的字段hashCode()
,我编写了一个名为“stem”的小助手(基本核心数据,与平等相关)。
用法很简单,生成的代码非常小:
class Person(val id: String, val name: String) {
private val stem = Stem(this, { id })
override fun equals(other: Any?) = stem.eq(other)
override fun hashCode() = stem.hc()
}
可以通过额外的即时计算来权衡存储在类中的支持字段:
private val stem get() = Stem(this, { id })
由于Stem
采用任何函数,因此您可以自由指定如何计算相等性。要考虑多个字段,只需为每个字段添加一个 lambda 表达式(可变参数):
private val stem = Stem(this, { id }, { name })
执行:
class Stem<T : Any>(
private val thisObj: T,
private vararg val properties: T.() -> Any?
) {
fun eq(other: Any?): Boolean {
if (thisObj === other)
return true
if (thisObj.javaClass != other?.javaClass)
return false
// cast is safe, because this is T and other's class was checked for equality with T
@Suppress("UNCHECKED_CAST")
other as T
return properties.all { thisObj.it() == other.it() }
}
fun hc(): Int {
// Fast implementation without collection copies, based on java.util.Arrays.hashCode()
var result = 1
for (element in properties) {
val value = thisObj.element()
result = 31 * result + (value?.hashCode() ?: 0)
}
return result
}
@Deprecated("Not accessible; use eq()", ReplaceWith("this.eq(other)"), DeprecationLevel.ERROR)
override fun equals(other: Any?): Boolean =
throw UnsupportedOperationException("Stem.equals() not supported; call eq() instead")
@Deprecated("Not accessible; use hc()", ReplaceWith("this.hc(other)"), DeprecationLevel.ERROR)
override fun hashCode(): Int =
throw UnsupportedOperationException("Stem.hashCode() not supported; call hc() instead")
}
如果您想知道最后两种方法,它们的存在会使以下错误代码在编译时失败:
override fun equals(other: Any?) = stem.equals(other)
override fun hashCode() = stem.hashCode()
如果这些方法被隐式调用或通过反射调用,则异常只是一个后备;必要时可以争论。
当然,Stem
该类可以进一步扩展以包括自动生成toString()
等。