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:
{KOTLIN_OOP} {KOTLIN_MAIN_BLOCK} val s = Student("Pavel Hora", true, 20) println(s) {/KOTLIN_MAIN_BLOCK} {/KOTLIN_OOP}
{KOTLIN_OOP} 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ý." } } {/KOTLIN_OOP}
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):
{KOTLIN_OOP} {KOTLIN_MAIN_BLOCK} val s = Student("Pavel Hora", true, 20) s.vek = 15 s.muz = false println(s) {/KOTLIN_MAIN_BLOCK} {/KOTLIN_OOP}
{KOTLIN_OOP} 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ý." } } {/KOTLIN_OOP}
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:
{KOTLIN_OOP} {KOTLIN_MAIN_BLOCK} 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) {/KOTLIN_MAIN_BLOCK} {/KOTLIN_OOP}
{KOTLIN_OOP} 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ý." } } {/KOTLIN_OOP}
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