Lekce 10 - Vlastnosti v Kotlin

Kotlin Objektově orientované programování Vlastnosti v Kotlin

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Companion objects v Kotlin, jsme si vysvětlili companion objects. 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)

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)

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)

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 příští lekci, Datum a čas v Kotlin - Vytváření a formátování, si naprogramujeme databázi pomocí Array, bude to elektronický diář! :)


 

Stáhnout

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

 

 

Článek pro vás napsal Samuel Kodytek
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
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 (3)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!