IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

10. diel - Vlastnosti v Kotlin

V predchádzajúcom cvičení, Riešené úlohy k 9. lekcii OOP v Kotlin, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V minulej lekcii, Riešené úlohy k 9. lekcii OOP v Kotlin , sme si vysvetlili companion objects. V dnešnom Kotlin tutoriálu sa pozrieme na ďalšie prvky tried, ktoré ešte nepoznáme. Začnime prisľúbeným vlastnosťami.

Vlastnosti

Veľmi často sa nám stáva, že chceme mať kontrolu nad zmenami nejakého atribútu objektu zvonku. Budeme chcieť atribút nastaviť ako read-only alebo reagovať na jeho zmeny. Založme si nový projekt (názov Vlastnosti) a vytvorme nasledujúce triedu Student, ktorá bude reprezentovať študenta v nejakom informačnom systéme.

Pre potreby tutoriálu budeme v triede používať iba var, pretože budeme počítať s tým, že sa dáta v premenných budú meniť s editáciou osoby. Zároveň sa však nad týmito zmenami budeme snažiť získať kontrolu.

class Student(var jmeno: String, var muz: Boolean, var vek: Int) {

    var plnolety: Boolean

    init {
        plnolety = true
        if (vek < 18)
            plnolety = false
    }

    override fun toString(): String {
        var jsemPlnolety = "jsem"
        if (!plnolety)
            jsemPlnolety = "nejsem"
        var pohlavi = "muž"
        if (!muz)
            pohlavi = "žena"
        return "Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý."
    }
}

Trieda je veľmi jednoduchá, študent sa nejako volá, je nejakého pohlavia a má určitý vek. Podľa tohto veku sa nastavuje atribút plnolety pre pohodlnejšie vyhodnocovanie plnoletosti na rôznych miestach systému. Na uloženie pohlavia používame hodnotu typu Boolean, či je študent muž. Konštruktor podľa veku určí, či je študent plnoletý. Metóda toString() je navrhnutá pre potreby tutoriálu tak, aby nám vypísala všetky informácie. V reáli by vrátila pravdepodobne len meno študenta. Pomocou konstruktoru si nejakého študenta vytvorme:

val s = Student("Pavel Hora", true, 20)
println(s)
class Student(var jmeno: String, var muz: Boolean, var vek: Int) {

    var plnolety: Boolean

    init {
        plnolety = true
        if (vek < 18)
            plnolety = false
    }

    override fun toString(): String {
        var jsemPlnolety = "jsem"
        if (!plnolety)
            jsemPlnolety = "nejsem"
        var pohlavi = "muž"
        if (!muz)
            pohlavi = "žena"
        return "Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý."
    }
}

výstup:

Jsem Pavel Hora, muž. Je mi 20 let a jsem plnoletý.

Všetko vyzerá pekne, ale atribúty sú prístupné ako na čítanie, tak na zápis. Objekt teda môžeme rozbiť napríklad takto (hovoríme o nekonzistentnom vnútorným stave):

val s = Student("Pavel Hora", true, 20)
s.vek = 15
s.muz = false
println(s)
class Student(var jmeno: String, var muz: Boolean, var vek: Int) {

    var plnolety: Boolean

    init {
        plnolety = true
        if (vek < 18)
            plnolety = false
    }

    override fun toString(): String {
        var jsemPlnolety = "jsem"
        if (!plnolety)
            jsemPlnolety = "nejsem"
        var pohlavi = "muž"
        if (!muz)
            pohlavi = "žena"
        return "Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý."
    }
}

výstup:

Jsem Pavel Hora, žena. Je mi 15 let a jsem plnoletý.

Určite musíme ošetriť, aby sa plnoletosť obnovila pri zmene veku. Keď sa zamyslíme nad ostatnými atribúty, nie je najmenší dôvod, aby sme taktiež umožňovali modifikovať pohlavia. Bolo by však zároveň vhodné ich vystaviť na čítanie, nemôžeme je teda iba nastaviť ako private. V skorších lekciách kurze sme na tento účel používali metódy, ktoré slúžili na čítanie privátnych atribútov, alebo verejné val premenné. Názov metód sme volili ako vratVek() a podobne. Na čítanie vybraných vlastností teda vytvoríme tiež metódy a vlastnosti označíme ako privátne, aby sa nedali modifikovať zvonku. Trieda by novo vyzerala napr. Takto (vynechal som konštruktor a toString()):

class Student(private var jmeno: String, private var muz: Boolean, private var vek: Int) {

    var plnolety: Boolean

    fun vratJmeno(): String {
        return jmeno
    }

    fun vratPlnoletost(): Boolean {
        return plnolety
    }

    fun vratVek(): Int {
        return vek
    }

    fun jeMuz(): Boolean {
        return muz
    }

    fun nastavVek(hodnota: Int) {
        vek = hodnota
        // přehodnocení plnoletosti
        plnolety = true
        if (vek < 18) {
            plnolety = false
        }
    }
}

Metódy, čo hodnoty len vracajú, sú veľmi jednoduché. Nastavenie veku má už nejakú vnútornú logiku, pri jeho zmene musíme totiž prehodnotiť atribút plnolety. Zaistili sme, že sa do premenných nedá zapisovať inak, ako my chceme. Máme teda pod kontrolou všetky zmeny atribútov a dokážeme na ne reagovať. Nemôže sa stať, že by nám niekto vnútorný stav nekontrolovane menil a rozbil.

Metódam na vrátenie hodnoty sa hovorí Getter a metódam pre zápis setter. Pre editáciu ostatných atribútov by sme urobili jednu metódu editujStudenta(), ktorá by bola podobná konstruktoru. Meno, vek a podobne by sa teda menili pomocou tejto metódy, tam by sme mohli napr. Kontrolovať, či hodnoty dávajú zmysel, opäť by sme odchytili všetky pokusy o zmenu na jedinom mieste.

Pýtania sa na vlastnosti pomocou metód je však prácne a môže byť aj mätúce. Kotlín preto obsahuje pre vlastnosti ďalšie syntax.

Private set

Ak chceme len zabrániť tomu, aby bolo vlastnosť možné nastavovať zvonku, pomôže nám modifikátor prístupu private set. Ten teraz umiestnime pred vlastnosti našej triedy, zachováme tým perfektné zapuzdrenie a Getter (metódy) pre tieto vlastnosti už nebudeme potrebovať, preto ich odstránime.

Kotlín nám neumožňuje nastaviť tento modifikátor v skrátenej verzii konstruktoru, preto si vlastnosti definujeme až v tele triedy.

class Student(jmeno: String, muz: Boolean, vek: Int) {

    var jmeno = jmeno
        private set

    var muz = muz
        private set

    var vek = vek
        private set

    var plnolety: Boolean
        private set

    // Zbytek implementace...
}

Vnútri triedy sa vlastnosti správajú štandardne, ale zvonku ich nemôžeme meniť.

Backing vlastnosti

Elegantne sme zamedzili nechceným modifikáciám vlastností našej triedy zvonku. Inou technikou môžeme docieliť tiež toho, že sa vlastnosť chová ako metóda, ale keď sa na nej pýtame, nepíšeme za jej názvom zátvorku (). To sa hodí pre vlastnosti, ktoré vracajú hodnotu na základe iných vlastností. Takýmto vlastnostiam v Kotlin hovoríme Backing vlastnosti (Backing properties). V našej triede je využijeme pre plnoletost, kde namiesto uložené Boolean hodnoty vrátime priamo výraz vek < 18. Tým bude hodnota vlastnosti vždy aktuálne. Upravme teda vlastnosť plnolety:

var plnolety: Boolean = false
    get() {
        return vek >= 18
    }

Všimnite si, že vlastnosť plnolety je stále premenná a preto jej musíme inicializovať hodnotou false alebo true a to aj keď sa táto hodnota v skutočnosti nikdy nepoužije.

Trieda po všetkých úpravách vyššie bude teraz vyzerať takto:

class Student(jmeno: String, muz: Boolean, vek: Int) {

    var jmeno = jmeno
        private set

    var muz = muz
        private set

    var vek = vek
        private set

    var plnolety: Boolean = false
        private set
        get() {
            return vek >= 18
        }

    override fun toString(): String {
        var jsemPlnolety = "jsem"
        if (!plnolety)
            jsemPlnolety = "nejsem"
        var pohlavi = "muž"
        if (!muz)
            pohlavi = "žena"
        return "Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý."
    }
}

Týmto sme napísali jednoduchý getter, ktorý v Kotlinu zvonku vyzerá ako obyčajná vlastnosť, ale možno ju len čítať.

Samozrejme, ak by ste v Getter mali náročnejšie výpočet a pristupovali k vlastnosti často, tak sa oplatí zamyslieť nad optimalizáciou a výsledok počítať pri nastavení vlastností, z ktorých sa vychádza. V našom prípade je to vlastnosť vek.

Vyzeralo by to napr. Nasledovne:

var vek = vek
    set(value) {
        plnolety = vek >= 18
        field = value // Nastavíme `vek` na `value`
    }

V tomto prípade je set(value) public, keby sme chceli zamedziť zmenám zvonku, môžeme použiť modifikátor prístupu private.

Keby sme sa pokúšali nastaviť vek na value (novú hodnotu), zacyklí sa nám program!

Skúsme si teraz ešte spustiť kód, ktorý predtým rozbil interný stav objektu:

val s = Student("Pavel Hora", true, 20)
s.vek = 15
//s.muz = false // Tento řádek musíme zakomentovat, jelikož se pohlaví již nedá zvenčí změnit
println(s)
class Student(jmeno: String, muz: Boolean, vek: Int) {

    var jmeno = jmeno
        private set

    var muz = muz
        private set

    var vek = vek
            set(value) {
            plnolety = vek >= 18
            field = value // Nastavíme `vek` na `value`
            }

    var plnolety: Boolean = false
        private set
        get() {
            return vek >= 18
        }

    override fun toString(): String {
        var jsemPlnolety = "jsem"
        if (!plnolety)
            jsemPlnolety = "nejsem"
        var pohlavi = "muž"
        if (!muz)
            pohlavi = "žena"
        return "Jsem $jmeno, $pohlavi. Je mi $vek let a $jsemPlnolety plnoletý."
    }
}

Výstup je už v poriadku:

Jsem Pavel Hora, muž. Je mi 15 let a nejsem plnoletý.

V Jave sa Kotlin vlastnosti zobrazujú ako getNazevVlastnosti() a alebo setNazevVlastnosti(). Je to z toho dôvodu, že Kotlin na pozadí naozaj generuje Getter a setter, ktoré používa Java.

V budúcej lekcii, Dátum a čas v Kotlin - Vytváranie a formátovanie , si naprogramujeme databázu pomocou Array, bude to elektronický diár! :)


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 17x (15.85 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin

 

Predchádzajúci článok
Riešené úlohy k 9. lekcii OOP v Kotlin
Všetky články v sekcii
Objektovo orientované programovanie v Kotlin
Preskočiť článok
(neodporúčame)
Dátum a čas v Kotlin - Vytváranie a formátovanie
Článok pre vás napísal Samuel Kodytek
Avatar
Užívateľské hodnotenie:
1 hlasov
Autor se věnuje všem jazykům okolo JVM. Rád pomáhá lidem, kteří se zajímají o programování. Věří, že všichni mají šanci se naučit programovat, jen je potřeba prorazit tu bariéru, který se říká lenost.
Aktivity