里氏替换原则如何应用于函数返回类型?

ser*_*0ne 1 liskov-substitution-principle solid-principles

里氏替换原则指出:

程序中的对象应该可以用其子类型的实例替换,而不改变该程序的正确性。

假如说:

interface Iterable<T> {
    fun getIterator(): Iterator<T>
}

interface Collection<T> : Iterable<T> {
    val size: Int
}

interface List<T> : Collection<T> {
    fun get(index: Int): T
}

interface MutableList<T> : List<T> {
    fun set(index: Int, item: T): Unit
}
Run Code Online (Sandbox Code Playgroud)

当 LSP 应用于输入参数时,应应用最低级别的抽象:

fun foo(items: Iterable<Any>) { ... }
Run Code Online (Sandbox Code Playgroud)

fun foo(items: List<Any>) { ... }
Run Code Online (Sandbox Code Playgroud)

但是,LSP 是否适用于函数返回类型?如果适用,则反之亦然吗?

fun bar(): Iterable<Any> { ... }
Run Code Online (Sandbox Code Playgroud)

或者

fun bar(): List<Any> { ... }
Run Code Online (Sandbox Code Playgroud)

Com*_*ubh 5

是的,是的。正如您所指出的,为了遵守 LSP,重写方法中的参数类型必须是逆变的。对于返回类型来说,反之亦然——这必须是协变的,即与被覆盖的方法中的返回类型具有相同的类型,或更具体的类型。

想想这句口号:“要求不多,承诺不少”。假设超类方法返回一个 Rectangle。可以重写此方法以返回 Square,因为这“承诺更多”,但不能返回 Shape,因为这将“承诺更少”。