Как принцип подстановки Лискова применяется к типам, возвращаемым функцией?

Принцип замещения Лискова гласит, что:

Объекты в программе должны заменяться экземплярами их подтипов без изменения правильности этой программы.

При условии, что:

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
}

Когда LSP применяется к входным параметрам, должна применяться абстракция самого низкого уровня:

ДЕЛАТЬ

fun foo(items: Iterable<Any>) { ... }

НЕЛЬЗЯ

fun foo(items: List<Any>) { ... }

Но применяется ли LSP к типам возвращаемых функций, и если да, то применяется ли обратное?

fun bar(): Iterable<Any> { ... }

ИЛИ

fun bar(): List<Any> { ... }

person Matthew Layton    schedule 09.05.2019    source источник
comment
Это DIP, а не LSP; вы программируете на абстракцию. Вы также можете прочитать о ко- и контр-дисперсии, например в docs.microsoft.com/en-us/dotnet / стандартный / дженерики /.   -  person jonrsharpe    schedule 09.05.2019


Ответы (1)


Да и да. Как вы указываете, для соответствия LSP типы аргументов в методе переопределения должны быть контравариантными. Обратное верно для типа возвращаемого значения - он должен быть ковариантным, то есть иметь тот же или более конкретный тип, что и тип возвращаемого значения в переопределяемом методе.

Вспомните лозунг «Не требуй больше, не обещай меньше». Допустим, метод суперкласса возвращает Rectangle. Этот метод можно переопределить для возврата Square, поскольку он «обещает больше», но не для возврата Shape, поскольку это «обещает меньше».

person ComDubh    schedule 12.05.2019