I'm trying to implement a ValueObject base class using Kotlin and DDD. As any value object must consider all attributes in the equals method, I decided to implement a single method using reflection, so no subclass will need to implement it.
My ValueObject class is as follows:
abstract class ValueObject {
override fun equals(other: Any?) = when {
this === other -> true
other !is ValueObject -> false
else -> deepEquals(other)
}
inline fun <reified T : Any> deepEquals(other: T): Boolean {
val fields = T::class.java.declaredFields
println(T::class)
for (field in fields) {
field.isAccessible = true
val thisValue = field.get(this)
val otherValue = field.get(other)
if (thisValue != otherValue && (thisValue == null || !thisValue.equals(otherValue))) {
return false
}
}
return true
}
override fun hashCode() = hashFromReflection()
private fun hashFromReflection(): Int {
val fields = this::class.java.declaredFields
var result = 17
for (field in fields) {
field.isAccessible = true
val value = field.get(this)
result = 31 * result + (value?.hashCode() ?: 0)
}
return result
}
protected abstract fun validate() : Notification
}
I implemented the following code to test:
class PhoneNumber (val code: String, val number: String) : ValueObject() {
override fun validate(): Notification {
return Notification()
}
}
fun main() {
val phoneNumber = PhoneNumber("33", "w")
val other = PhoneNumber("3", "w")
println(phoneNumber == other)
println(phoneNumber.deepEquals(other))
}
However, the result is weird. If I call deepEquals directly, it works fine. But if I use == operator, it considers T as ValueObject (not PhoneNumber), finds no declaredFields, and gives me a wrong result:
class br.all.domain.common.ValueObject
true
class br.all.domain.common.PhoneNumber
false
What I'm doing wrong?
You seem to be overcomplicating this with
reified. You can just get theClassof objects by doingthis::class.javaorother::class.java. There is no need forreified.The reason why your code doesn't work is because there is no type information about the specific subclass of
ValueObjectwhen you calldeepEquals(other)inequals. The reified type parameter is simply passed asdeepEquals<ValueObject>(other).Also, you seem to be reinventing data classes. Consider doing this with data classes instead.