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 ООП обмеження узагальнень


Обмеження узагальнень (generic constraints) обмежують набір типів, які можуть передаватися замість параметра в узагальненнях.

Наприклад, у нас є наступний клас Account, який представляє банківський рахунок:

open class Account(_id: Int, _sum: Int){
    val id = _id
    var sum = _sum
}
class DepositAccount(_id: Int, _sum: Int) : Account(_id, _sum)

Для переказу коштів з одного рахунку на інший ми можемо визначити клас Transaction, який для виконання всіх операцій буде використовувати об'єкти класу Account. Однак у класу Account може бути багато спадкоємців: DepositAccount (депозитний рахунок), DemandAccount (рахунок до запитання) і т.д. І ми не можемо знати, які саме типи рахунків будуть використовуватися в класі Transaction. Можливо, транзаціі будуть проводитися тільки між рахунками до запитання. І в цьому випадку в якості універсального параметра можна встановити тип Account:

class Transaction<T : Account>(_from: T, _to: T, _sum: Int){
 
    val fromAcc = _from  // с какого счета перевод
    val toAcc = _to      // на какой счет перевод
    val sum = _sum      // сумма перевода
    fun execute(){
        if(fromAcc.sum >= sum){
            fromAcc.sum -= sum
            toAcc.sum += sum
            println("Transaction completed")
        }
        else{
            println("Transaction failed")
        }
    }
}

За допомогою виразу T : Account вказується, що параметр T обов'язково повинен представляти клас Account або його спадкоємців. Завдяки даному обмеження ми можемо використовувати всередині класу Transaction всі об'єкти типу T саме як об'єкти Account і відповідно звертатися до їх властивостей і методів.

Тепер застосуємо клас Transaction в програмі:

fun main(args: Array<String>) {
 
    var acc1 = Account(2, 300)
    var acc2  = Account(745, 600)
    var t1 = Transaction(acc1, acc2, 200)
    var t2 = Transaction(acc1, acc2, 1200)
    t1.execute()
    t2.execute()
}

Подібним чином можна використовувати і обмеження функцій:

fun main(args: Array<String>) {
 
    var acc1 = Account(2, 300)
    var acc2  = Account(745, 600)
    transfer(acc1, acc2, 200)
    transfer(acc1, acc2, 1200)
}
fun <T:Account> transfer(fromAcc: T, toAcc: T, sum: Int){
    if(fromAcc.sum >= sum){
        fromAcc.sum -= sum
        toAcc.sum += sum
        println("Transaction completed")
    }
    else{
        println("Transaction failed")
    }
}

В даному випадку функція transfer як обмеження використовує клас Account.

Інший приклад. Для складання двох чисел ми можемо визначити кілька функцій - одну для складання значень Int, другу - для складання об'єктів Double і так далі. А можна визначити одну узагальнену функцію, яка буде обмежена типом Number:

fun <T: Number> sum(a: T, b: T): Double{
    return a.toDouble() + b.toDouble()
}

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