【问题标题】:Kotlin data class inheritance cast to parent equal is wrongKotlin 数据类继承强制转换为父相等是错误的
【发布时间】:2022-01-24 19:31:27
【问题描述】:

我想知道为什么 equal 方法 (==) 不能按预期工作。

有没有办法修复下面代码中的注释部分?

如您所见,p1 和 p2 不相等,既不是引用也不是值。那么为什么 p1 == p2 是真的呢?!

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()
    }
}

【问题讨论】:

    标签: kotlin


    【解决方案1】:

    有一种解决方案,您必须在 Foo 类中显式覆盖 equals 和 hashCode 方法。然后就可以正常工作了。

    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()
        }
    }
    

    【讨论】:

      【解决方案2】:

      这不符合您预期的原因是因为data class 自动为您提供equals() 的实现,它(仅)检查其构造函数中指定的属性的值。

      所以你的data class Foo(val a: Int) 得到一个equals(),它只检查a 的值,覆盖Parent 类中的值。

      一般来说,data classes 是简单的值持有者,其值完全由这些属性来表征,因此自动生成的 equals()(和 hashCode()toString()copy()componentX()方法)很有意义。如果这不适用,data class 可能不适合您的情况。

      【讨论】:

      • Kotlin 文档提到的关于数据类的唯一限制是:数据类不能是抽象的、开放的、密封的或内部的。因此,它应该可以正常工作,但正如您所见,事实并非如此。想象一下,我没有测试它并在生产中使用它,那么由于生成的代码错误,项目的行为会有所不同。
      • 我认为这句话应该添加到 Kotlin 文档中:不要对数据类使用继承。正如@Andrey Breslav 在这里提到的:stackoverflow.com/a/26467380/6751083
      • 这个建议很老了。数据类有父类是有正当理由的。密封类有数据类子级是很常见的。 (也许现在有了密封接口,这种情况会变得不那么常见。)您只需要注意,equals/hashcode 实现仅包含数据类的主构造函数的属性。
      • 我什至检查了密封类,它不能正常工作。我知道有很多方法可以正确使用数据类。尽管如此,我还是不能接受 Kotlin 让它只使用一半的功能或在文档中没有明确警告。
      • 它根据数据类所做的定义工作,因此无论这是否是最佳设计,它都能正常工作。文档清楚地表明生成的函数只查看主构造函数中的属性。
      猜你喜欢
      • 1970-01-01
      • 2012-04-21
      • 2021-11-28
      • 1970-01-01
      相关资源
      最近更新 更多