Интерфейсы делегированных свойств не очень полезны для иллюстрации отклонений, потому что они являются необязательными, и вы никогда не вызываете эти функции напрямую. И вы вряд ли построите иерархию классов с ними или назначите их экземпляры переменным.
Дисперсия в этом случае бесполезна, а просто предполагает совместимость.
В случае делегатов thisRef
имеет контравариантный тип, указывающий, что делегат только для чтения, который предназначен для работы в определенном классе, также может использоваться в подклассах этого класса.
Предположим, у вас есть класс Cat, который мяукает, и реализация делегата, которая делает его мяу, когда вы читаете делегированное свойство:
open class Cat {
fun meow() {
println("meow")
}
}
class MeowingDelegate<T>(private val value: T): ReadOnlyProperty<Cat, T> {
override fun getValue(thisRef: Cat, property: KProperty<*>): T {
thisRef.meow()
return value
}
}
Вы можете использовать этот делегат в подклассе Cat
, потому что подкласс будет иметь методы суперкласса, поэтому подкласс также может мяукать. Подпись предполагает, что MeowingDelegate с его типом in Cat
квалифицируется как подкласс делегата с более конкретным типом c и поэтому может использоваться в подклассе Cat.
open class Kitten: Cat() {
val x: Int by MeowingDelegate(1)
}
Редактировать: Для коварианта T
для ReadOnlyProperty это означает, что делегат, который предоставляет один тип объекта, может также использоваться в качестве поставщика супертипа этого объекта. Поэтому, если бы у меня был делегат, который предоставляет котят, его также можно использовать для предоставления кошек:
open class Cat
open class Kitten: Cat()
class KittenDelegate: ReadOnlyProperty<Any?, Kitten> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Kitten {
return Kitten()
}
}
class Sample {
val cat: Cat by KittenDelegate()
}
Поскольку тип T
является инвариантным для ReadWriteProperty, вы не можете сделать это с делегатом, который можно только вернуть котят. Это имеет смысл, потому что делегат также должен позволять вам устанавливать свойство, и поэтому он не сможет принять любого кота и рассматривать его как котенка.
Опять же, эти интерфейсы делегатов не велики примеры для понимания дисперсии, поскольку они обычно не используются в качестве интерфейсов. Для более практического примера вы можете посмотреть на знакомые классы коллекций.
Только для чтения List
имеет ковариантный тип. Это позволяет вам легко приводить коллекции к более конкретным c типам:
class Kennel (val cats: List<Cats>)
val kittens: List<Kitten> = listOf(Kitten(), Kitten())
val kennel = Kennel(kittens) // OK
Вы не можете сделать это с MutableList
, так как тип является инвариантным.
class Kennel (val cats: MutableList<Cats>)
val kittens: MutableList<Kitten> = mutableListOf(Kitten(), Kitten())
val kennel = Kennel(kittens) // Compiler error
Но это необходимо, чтобы MutableList был определен с инвариантным типом. MutableList<Kitten>
не может принять любой тип Cat, который будет добавлен к нему, поэтому не имеет смысла иметь возможность привести его к MutableList<Cat>
. И наоборот, MutableList<Cat>
не обязательно содержит исключительно котят, поэтому нет смысла приводить его к MutableList<Kitten>
. Конечно, на сайте использования вы можете дать ему дисперсию в зависимости от ваших потребностей, и именно поэтому MutableList<Kitten>
может быть приведено к MutableList<out Cat>
.
Для контравариантности на уровне класса я могу не думаю о распространенном классе stdlib, который есть в объявлении. Итак, предположим, мы создали тот, который домашние животные кошек. Тогда у нас есть котята, которых мы хотим погладить. CatPetter
было бы достаточно для использования в качестве Petter<Kitten>
, потому что котята считаются кошками и поэтому могут быть домашними животными на CatPetter
. Если бы тип Petter
был инвариантным, это было бы недопустимо.
open class Cat {
fun purr() {
println("rrrr")
}
}
open class Kitten: Cat()
interface Petter<in T: Cat> {
fun pet(recipient: T)
}
class CatPetter: Petter<Cat> {
override fun pet(recipient: Cat) {
recipient.purr()
}
}
class BoxOfKittens (val petter: Petter<Kitten>)
val x = BoxOfKittens(CatPetter()) // OK