Kotlin в IntelliJ IDEA Kotlin змінні Kotlin типи даних Kotlin операції з числами Kotlin умовні вирази Kotlin умовні конструкції Kotlin цикли Kotlin послідовності Kotlin масиви Kotlin функціональне програмування Kotlin змінна кількість параметрів. Vararg Kotlin повернення результату. оператор return Kotlin однорядкові і локальні функції Kotlin перевантаження функцій Kotlin Лямбда-вирази Kotlin функції вищого порядку Kotlin Анонімні функції Kotlin ООП Класи і об'єкти Kotlin ООП властивості Kotlin ООП конструктор Kotlin ООП функції в класах Kotlin ООП Пакети та імпорт Kotlin ООП модифікатори видимості Kotlin ООП вкладені класи Kotlin ООП інтерфейси Kotlin ООП спадкування Kotlin ООП Перевизначення методів і властивостей Kotlin ООП Абстрактні класи та методи Kotlin ООП Data-класи Kotlin ООП перерахування enums Kotlin ООП Null і nullable-типи Kotlin ООП Обробка винятків Kotlin ООП перетворення типів Kotlin ООП Інфіксна функція Kotlin ООП функції розширення Kotlin ООП Узагальнені класи і функції Kotlin ООП обмеження узагальнень Kotlin ООП Варіантність, коваріантність і контраваріантність Kotlin Колекції. Змінювані і незмінні колекції Kotlin Колекції. LIST список Kotlin Колекції. Set Kotlin Колекції. Map

Kotlin ООП Варіантність, коваріантність і контраваріантність



Варіантність, коваріантність і контраваріантних

Варіантність описує, як узагальнені типи, типізовані класами з однієї ієрархії успадкування, співвідносяться один з одним.


Інваріантність

Інваріантність передбачає, що, якщо у нас є класи A і B, то клас D <A> не є ні базовим, ні похідним класом для D <B>.

fun main(args: Array<String>) {
 
    var bank1: Bank<Account> = Bank()
    var bank2: Bank<Deposit> = Bank()
     
    // bank1 = bank2   // так нельзя сделать
    // bank2 = bank1   // так нельзя
}
class Bank<T>
 
open class Account(_sum: Int){
    var sum = _sum
}
class Deposit(sum: Int): Account(sum)

В даному випадку ми не можемо привласнити об'єкт Bank<Deposit> змінної типу Bank<Account> , вони ніяк між собою не співвідносяться, незважаючи на те, що Deposit успадковується від Account.


Kovariantost '

Коваріантость передбачає, що, якщо у нас є класи A і B, то клас D <A> є похідним класом для D <B>, якщо A є похідним класом для B.

Для визначення узагальненого типу як коваріантного параметр обощения визначається з ключовим словом out :

fun main(args: Array<String>) {
 
    var bank1: IBank<Account> = AccountBank()
    var bank2: IBank<Deposit> = DepositBank()
     
    var dep : Deposit = bank2.produce(200)
    var acc : Account = bank1.produce(100)
 
     bank1 = bank2   // IBank<Deposit> производный от IBank<Account>
    //bank2 = bank1   // так нельзя
}
interface IBank<out T>{
    fun produce(sum: Int): T
}
class DepositBank: IBank<Deposit>{
 
    override fun produce(sum: Int): Deposit{
        return Deposit(sum)
    }
}
class AccountBank: IBank<Account>{
 
    override fun produce(sum: Int): Account{
        return Account(sum)
    }
}
 
open class Account(_sum: Int){
    var sum = _sum
}
class Deposit(sum: Int): Account(sum)

В даному випадку інтерфейс IBank є коваріантним , так як його параметр визначений зі словом out: interface IBank<out T> . Взагалі не випадково використовується саме слово out . Воно вказує, що параметр представляє тип, який повинен бути повертається типом. Тобто ми можемо визначити функцію, яка повертає об'єкт типу T, як в даному випадку функція produce:

fun produce(sum: Int): T

У той же час тип T не слід використовувати в якості типу вхідних параметрів функції. Тобто параметр коваріантного типу визначає тип взвращаемих значень.


контраваріантних

Контраваріантость передбачає, що, якщо у нас є класи A, похідний від B, то клас D <A> є супертіп для D <B>, якщо D визначено як контраваріантний. Тобто має місце інверсія: з одного боку, A похідний від B, але з іншого боку, D <B> є похідним від D <A>.

Для визначення узагальненого типу як контраваріантного параметр узагальнення визначається з ключовим словом in :

fun main(args: Array<String>) {
 
    var bank1: Bank<Account> = Bank()
    var bank2: Bank<Deposit> = Bank()
 
    var acc = Deposit(300)
    bank1.consume(acc, 400)
    println(acc.sum)
 
    // bank1 = bank2    // так нельзя
    bank2 = bank1   // Bank<Account> производный от Bank<Deposit>
 
}
 
class Bank<in T : Account>{
    fun consume(acc: T, sum: Int){
        acc.sum += sum
    }
}
 
open class Account(_sum: Int){
    var sum = _sum
}
class Deposit(sum: Int): Account(sum)

Тут клас Bank визначено як контраваріантний за допомогою оператора in : class Bank<in T : Account> . Слово in вказує, що тип, який буде передаватися через параметр T, належним бути вхідним, тобто представляти тип вхідних параметрів функції. В даному випадку такою функцією є consume:

fun consume(acc: T, sum: Int){
    acc.sum += sum
}

Наш партнер:
beta test mp3 playlist downloader