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
.
对于 returns 和 Foo
的函数,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。
我们来看两种情况:
- 函数
getFoo()
returns Foo。在一个子类型中,你 return 吧。由于每个 Bar 都是一个 Foo(因为 Bar 是 Foo 的子类型),您可以在每个使用基本版本的地方使用 getFoo() 的覆盖版本 - 它总是 returns 一个 Foo(恰好是Bar,一种特定类型的 Foo)。一切正常。
- 函数
setFoo()
接收一个 Foo。在子类型中,您希望它接受 Bar。现在,有人通过引用基本类型来使用您的子类型。他们传递给 setFoo()
一个不是 Bar 的 Foo。但是你的 setFoo()
在这种情况下只接受 Bar。这意味着你不能用你的 subclass 代替基础 class,这违反了 Liskov 替换原则并导致 subclassing 的整个想法崩溃。因此,语言的选择是不允许 subclass 接受 Bar 代替 Foo。
您还可以继续阅读 covariance and contravariance 以进一步探索可以使用子类型代替超类型的主题。
也许是我累了或者思维不清晰,或者这只是我以前从未尝试过的东西。
我有一个基本数据类型,我们称之为 Foo
,还有一个接口,我们称之为 Problem
,它公开了一些使用此 Foo
数据类型的函数。
一个接口函数returns一个Foo
和一个函数recieves一个Foo
.
然后,我继承自Foo
,创建一个新的数据类型,我们称它为Bar
,然后我实现Problem
接口,在重写中,我重写Foo
的类型为 Bar
.
对于 returns 和 Foo
的函数,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。
我们来看两种情况:
- 函数
getFoo()
returns Foo。在一个子类型中,你 return 吧。由于每个 Bar 都是一个 Foo(因为 Bar 是 Foo 的子类型),您可以在每个使用基本版本的地方使用 getFoo() 的覆盖版本 - 它总是 returns 一个 Foo(恰好是Bar,一种特定类型的 Foo)。一切正常。 - 函数
setFoo()
接收一个 Foo。在子类型中,您希望它接受 Bar。现在,有人通过引用基本类型来使用您的子类型。他们传递给setFoo()
一个不是 Bar 的 Foo。但是你的setFoo()
在这种情况下只接受 Bar。这意味着你不能用你的 subclass 代替基础 class,这违反了 Liskov 替换原则并导致 subclassing 的整个想法崩溃。因此,语言的选择是不允许 subclass 接受 Bar 代替 Foo。
您还可以继续阅读 covariance and contravariance 以进一步探索可以使用子类型代替超类型的主题。