Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 10 - Vlastnosti v Kotlin

V předešlém cvičení, Řešené úlohy k 9. lekci OOP v Kotlin, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V dnešním Kotlin tutoriálu se podíváme na další prvky tříd, které ještě neznáme. Začněme slíbenými vlastnostmi.

Vlastnosti

Velmi často se nám stává, že chceme mít kontrolu nad změnami nějakého atributu objektu zvenčí. Budeme chtít atribut nastavit jako read-only nebo reagovat na jeho změny. Založme si nový projekt (název Vlastnosti) a vytvořme následující třídu Student, která bude reprezentovat studenta v nějakém informačním systému.

Pro potřeby tutoriálu budeme ve třídě používat pouze var, jelikož budeme počítat s tím, že se data v proměnných budou měnit s editací osoby. Zároveň se ovšem nad těmito změnami budeme snažit získat 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ý."
    }
}

Třída je velmi jednoduchá, student se nějak jmenuje, je nějakého pohlaví a má určitý věk. Podle tohoto věku se nastavuje atribut plnolety pro pohodlnější vyhodnocování plnoletosti na různých místech systému. K uložení pohlaví používáme hodnotu typu Boolean, zda je student muž. Konstruktor dle věku určí, zda je student plnoletý. Metoda toString() je navržena pro potřeby tutoriálu tak, aby nám vypsala všechny informace. V reálu by vrátila pravděpodobně jen jméno studenta. Pomocí konstruktoru si nějakého studenta vytvořme:

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še vypadá hezky, ale atributy jsou přístupné jak ke čtení, tak k zápisu. Objekt tedy můžeme rozbít například takto (hovoříme o nekonzistentním vnitřním stavu):

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čitě musíme ošetřit, aby se plnoletost obnovila při změně věku. Když se zamyslíme nad ostatními atributy, není nejmenší důvod, abychom taktéž umožňovali modifikovat pohlaví. Bylo by však zároveň vhodné je vystavit ke čtení, nemůžeme je tedy pouze nastavit jako private. V dřívějších lekcích kurzu jsme k tomuto účelu používali metody, které sloužily ke čtení privátních atributů, nebo veřejné val proměnné. Název metod jsme volili jako vratVek() a podobně. Ke čtení vybraných vlastností tedy vytvoříme také metody a vlastnosti označíme jako privátní, aby se nedaly modifikovat zvenčí. Třída by nově vypadala např. takto (vynechal jsem konstruktor 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
        }
    }
}

Metody, co hodnoty jen vracejí, jsou velmi jednoduché. Nastavení věku má již nějakou vnitřní logiku, při jeho změně musíme totiž přehodnotit atribut plnolety. Zajistili jsme, že se do proměnných nedá zapisovat jinak, než my chceme. Máme tedy pod kontrolou všechny změny atributů a dokážeme na ně reagovat. Nemůže se stát, že by nám někdo vnitřní stav nekontrolovaně měnil a rozbil.

Metodám k navrácení hodnoty se říká gettery a metodám pro zápis settery. Pro editaci ostatních atributů bychom udělali jednu metodu editujStudenta(), která by byla podobná konstruktoru. Jméno, věk a podobně by se tedy měnily pomocí této metody, tam bychom mohli např. kontrolovat, zda hodnoty dávají smysl, opět bychom odchytili všechny pokusy o změnu na jediném místě.

Ptaní se na vlastnosti pomocí metod je ovšem pracné a může být i matoucí. Kotlin proto obsahuje pro vlastnosti další syntaxi.

private set

Pokud chceme pouze zamezit tomu, aby bylo vlastnost možné nastavovat zvenčí, pomůže nám modifikátor přístupu private set. Ten nyní umístíme před vlastnosti naší třídy, zachováme tím perfektní zapouzdření a gettery (metody) pro tyto vlastnosti již nebudeme potřebovat, proto je odstraníme.

Kotlin nám neumožňuje nastavit tento modifikátor ve zkrácené verzi konstruktoru, proto si vlastnosti definujeme až v těle třídy.

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...
}

Uvnitř třídy se vlastnosti chovají standardně, ale zvenčí je nemůžeme měnit.

Backing vlastnosti

Elegantně jsme zamezili nechtěným modifikacím vlastností naší třídy zvenčí. Jinou technikou můžeme docílit také toho, že se vlastnost chová jako metoda, ale když se na ní ptáme, nepíšeme za jejím názvem závorku (). To se hodí pro vlastnosti, které vracejí hodnotu na základě jiných vlastností. Takovýmto vlastnostem v Kotlin říkáme Backing vlastnosti (Backing properties). V naší třídě je využijeme pro plnoletost, kde místo uložené Boolean hodnoty vrátíme přímo výraz vek < 18. Tím bude hodnota vlastnosti vždy aktuální. Upravme tedy vlastnost plnolety:

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

Všimněte si, že vlastnost plnolety je stále proměnná a proto jí musíme inicializovat hodnotou false nebo true a to i když se tato hodnota ve skutečnosti nikdy nepoužije.

Třída po všech úpravách výše bude nyní vypadat 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 jsme napsali jednoduchý getter, který v Kotlinu zvenčí vypadá jako obyčejná vlastnost, ale lze ji pouze číst.

Samozřejmě, pokud byste v getteru měli náročnější výpočet a přistupovali k vlastnosti často, tak se vyplatí zamyslet nad optimalizací a výsledek počítat při nastavení vlastností, z kterých vychází. V našem případě je to vlastnost vek.

Vypadalo by to např. takto:

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

V tomto případě je set(value) public, kdybychom chtěli zamezit změnám zvenčí, můžeme použít modifikátor přístupu private.

Kdybychom se pokoušeli nastavit vek na value (novou hodnotu), zacyklí se nám program!

Zkusme si nyní ještě spustit kód, který předtí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 již v pořádku:

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

V Javě se Kotlin vlastnosti zobrazují jako getNazevVlastnosti() a nebo setNazevVlastnosti(). Je to z toho důvodu, že Kotlin na pozadí doopravdy generuje gettery a settery, které používá Java.

V následujícím kvízu, Kvíz - Dědičnost, statika, vlastnosti v Kotlin OOP, si vyzkoušíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 17x (15.85 kB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin

 

Předchozí článek
Řešené úlohy k 9. lekci OOP v Kotlin
Všechny články v sekci
Objektově orientované programování v Kotlin
Přeskočit článek
(nedoporučujeme)
Kvíz - Dědičnost, statika, vlastnosti v Kotlin OOP
Článek pro vás napsal Samuel Kodytek
Avatar
Uživatelské hodnocení:
15 hlasů
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