Home > other >  Kotlin data class inheritance cast to parent equal is wrong
Kotlin data class inheritance cast to parent equal is wrong

Time:12-24

I wonder why the equal method (==) does not work as expected.

Is there any way to fix the commented section in the code below?

As you can see p1 and p2 are not equals, neither reference nor value. So why p1 == p2 is true?!

object Main {
    @JvmStatic
    fun main(args: Array<String>) {

        val f1 = Foo(1)
        Thread.sleep(3) // to set a different value in parent of f2
        val f2 = Foo(1)

        val p1 = (f1 as Parent)
        val p2 = (f2 as Parent)

        println(p1 == p2) // true
        println(p1.b == p2.b) // false
    }
}

data class Foo(val a: Int) : Parent("$a-${System.currentTimeMillis()}")

sealed class Parent(val b: String) {

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Parent

        if (b != other.b) return false

        return true
    }
    override fun hashCode(): Int {
        return b.hashCode()
    }
}

CodePudding user response:

The reason this doesn't behave as you expect is because a data class automatically provides you with an implementation of equals(), one that checks (only) the values of the properties specified in its constructor.

So your data class Foo(val a: Int) gets an equals() that checks only the value of a, overriding that in the Parent class.

In general, data classes are simple value holders, whose value is characterised entirely by those properties, and so the automatically-generated equals() (and hashCode() and toString() and copy() and componentX() methods) make good sense. If that doesn't apply, a data class might not be a good fit for your case.

CodePudding user response:

There is one solution, and you have to override the equals and hashCode method explicitly in the Foo class. Then it will work fine.

object Main {
    @JvmStatic
    fun main(args: Array<String>) {

        val f1 = Foo(1)
        Thread.sleep(3)
        val f2 = Foo(1)

        val p1 = (f1 as Parent)
        val p2 = (f2 as Parent)

        println(p1 == p2) // false
        println(p1.b == p2.b) // false
    }
}

data class Foo(val a: Int) : Parent("$a-${System.currentTimeMillis()}") {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        if (!super.equals(other)) return false

        other as Foo

        if (a != other.a) return false

        return true
    }

    override fun hashCode(): Int {
        var result = super.hashCode()
        result = 31 * result   a
        return result
    }
}

sealed class Parent(val b: String) {

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as Parent

        if (b != other.b) return false

        return true
    }
    override fun hashCode(): Int {
        return b.hashCode()
    }
}
  • Related