Kotlin 多态性 - 接收参数不能覆盖类型,但 return 参数可以吗?

Kotlin polymorphism - receive argument cannot have type overriden, but return argument can?

也许是我累了或者思维不清晰,或者这只是我以前从未尝试过的东西。

我有一个基本数据类型,我们称之为 Foo,还有一个接口,我们称之为 Problem,它公开了一些使用此 Foo 数据类型的函数。

一个接口函数returns一个Foo和一个函数recieves一个Foo.

然后,我继承自Foo,创建一个新的数据类型,我们称它为Bar,然后我实现Problem接口,在重写中,我重写Foo 的类型为 Bar.

对于 returnsFoo 的函数,Bar 覆盖的用法有效。但是,对于 接收 Foo 的函数,Bar 覆盖的用法不起作用:

open class Foo(
    open val name: String
)

interface Problem {
    fun setFoo(foo: Foo)
    fun getFoo() : Foo
}

data class Bar(
    override val name: String
) : Foo(
    name = name
)

class ProblemImpl : Problem {
    override fun setFoo(foo: Bar) {
        TODO("Not yet implemented")
    }

    override fun getFoo(): Bar {
        TODO("Not yet implemented")
    }
}

setFoo overrides nothing

现在,这似乎很明显;只是不要覆盖这样的类型!您可以只传递 Bar 个实例,并且由于多态性,它们将被接收为 Foo 个实例。但是,由于复杂的原因,我不会在这里深入,我不能这样做(想想在库中声明 Dao 接口,并在使用 Room 注释的应用程序中覆盖它...)

我想我可以对接口进行类型参数化...例如:

interface Problem<T: Foo> {
   fun setFoo(foo: T)
   fun getFoo() : T
}

事实上,我想我刚刚回答了我自己的问题...我现在会尝试使用模板,但我有预感它会在编译时失败,因为 Room 注释处理器可能无法处理使用模板接口...在任何情况下,谁能告诉我为什么 receives 的函数不能被子类型覆盖,而 returns可以吗?

in any case, can anyone tell me why the function that receives cannot be overriden with a subtype whilst the function that returns can?

该限制是由于 Liskov substitution principle 引起的:如果 Bar 是 Foo 的子类型,那么在任何使用 Foo 的地方都应该可以使用 Bar。

我们来看两种情况:

  1. 函数 getFoo() returns Foo。在一个子类型中,你 return 吧。由于每个 Bar 都是一个 Foo(因为 Bar 是 Foo 的子类型),您可以在每个使用基本版本的地方使用 getFoo() 的覆盖版本 - 它总是 returns 一个 Foo(恰好是Bar,一种特定类型的 Foo)。一切正常。
  2. 函数 setFoo() 接收一个 Foo。在子类型中,您希望它接受 Bar。现在,有人通过引用基本类型来使用您的子类型。他们传递给 setFoo() 一个不是 Bar 的 Foo。但是你的 setFoo() 在这种情况下只接受 Bar。这意味着你不能用你的 subclass 代替基础 class,这违反了 Liskov 替换原则并导致 subclassing 的整个想法崩溃。因此,语言的选择是不允许 subclass 接受 Bar 代替 Foo。

您还可以继续阅读 covariance and contravariance 以进一步探索可以使用子类型代替超类型的主题。