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

3. diel - Hracia kocka v Kotlin - Konštruktory a náhodné čísla

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

V minulej lekcii, Riešené úlohy k 1.-2. lekciu OOP v Kotlin , sme si naprogramovali prvú objektovú aplikáciu. Už vieme tvoriť nové triedy a vkladať do nich atribúty a metódy s parametrami a návratovú hodnotou. V dnešnom tutoriále začneme pracovať na sľúbené aréne, v ktorej budú proti sebe bojovať dvaja bojovníci. Boj bude ťahový (na preskáčku) a bojovník vždy druhému uberie život na základe sily jeho útoku a obrany druhého bojovníka. Simulujeme v podstate stolný hru, budeme teda simulovať aj hraciu kocku, ktorá dodá hre prvok náhodnosti. Začnime zvoľna a vytvorme si dnes práve túto hraciu kocku. Zároveň sa naučíme ako definovať vlastné konštruktor.

Vytvorme si nový projekt, pomenujte ho TahovyBoj a vytvorme si všetky potrebné súčasti ako package a súbor Main.kt, ktorý bude obsahovať našu main() metódu. K projektu si pridajme novú class s názvom Kostka. Zamyslime sa nad atribúty, ktoré kocke dáme. Iste by sa hodilo, keby sme si mohli zvoliť počet stien kocky (klasicky 6 alebo 10 stien, ako je zvykom u tohto typu hier). Ďalej bude kocka potrebovať generovať náhodné čísla. To docielime pomocou rozsahu a metódy shuffled(). Naša trieda bude mať teraz len 1 atribút:

  • pocetSten typu int

Minule sme kvôli jednoduchosti nastavovali všetky atribúty našej triedy ako public, teda ako verejne prístupné. Väčšinou sa však skôr nechce, aby sa dali zvonku modifikovať a používa sa modifikátor private. Atribút je potom viditeľný len vnútri triedy a zvonku sa Kotlin tvári, že vôbec neexistuje. Pri návrhu triedy teda nastavíme všetko na private a v prípade, že niečo bude naozaj potrebné vystaviť, použijeme public. V Kotlin sa v prípade public modifikátor skôr vynecháva, pretože je predvolený. Naša trieda teraz vyzerá asi takto:

class Kostka {
    /** Počet stěn kostky */
    private val pocetSten: Int
}

Kód sa nám teraz podčiarkne červeno a nebude sa chcieť skompilovať. Kotlinu sa totiž nepáči, že sme si založili nový atribút bez nastavenej hodnoty. To sa vyrieši po tom, čo si napíšeme konštruktor a atribút pocetSten inicializujeme.

Konštruktory

Až doteraz sme nevedeli zvonku nastaviť iné atribúty ako public, pretože napr. private nie sú zvonku viditeľné. Už sme si hovorili niečo málo o konstruktoru objektu. Je to metóda, ktorá sa zavolá vo chvíli vytvorenia inštancie objektu. Slúži samozrejme k nastavenie vnútorného stavu objektu a na vykonanie prípadnej inicializácia. Kocku by sme teraz v Main.kt vytvorili takto:

fun main(args: Array<String>) {
    val kostka = Kostka() // v tuto chvíli se zavolá konstruktor
}

Práve Kostka() je konštruktor. Pretože v našej triede žiadny nie je, Kotlin si dogeneruje prázdnu metódu. My si však teraz konštruktor do triedy Kostka pridáme. Telo konstruktoru sa deklaruje kľúčovým slovíčkom init. V konstruktoru nastavíme počet stien na pevnú hodnotu. Konštruktor bude vyzerať nasledovne:

init {
    pocetSten = 6
}

Teraz naše aplikácie už pôjde preložiť.

Ak kocku vytvoríme, bude mať v atribúte pocetSten hodnotu 6. Vypíšme si počet stien do konzoly, nech vidíme, že tam hodnota naozaj je. Aby sme hodnotu mohli zvonku prečítať, nastavíme modifikátor atribútu pocetSten na public, resp. modifikátor prístupu odstránime. Že sme tým porušili zapuzdrenie? Ale nie. Máme atribút, ktorý sa nedá meniť (je imutabilní), vďaka slovíčku val. Jeho zverejnením sme v podstate iba docielili toho, že je atribút read-only (atribút je viditeľný a možno ho čítať, nemožno ho však už meniť). Tento princíp samozrejme platí len v prípade, keď atribút prvýkrát sami inicializujeme v konstruktoru. Kotlín má ešte ďalšie konštrukcie, ktoré sa na tento účel dajú použiť, ale tým sa zatiaľ nebudeme zaoberať. Deklarácia atribútu bude teda teraz vyzerať takto:

val pocetSten: Int

Presuňme sa do Main.kt a skúsme si vytvoriť kocku a vypísať počet stien:

fun main(args: Array<String>) {
    val kostka = Kostka() // v tuto chvíli se zavolá konstruktor
    println(kostka.pocetSten)
}

výstup:

6

Vidíme, že sa konštruktor naozaj zavolal. My by sme ale chceli, aby sme mohli pri každej kocky pri vytvorení špecifikovať, koľko stien budeme potrebovať. Dáme teda konstruktoru parameter. Parametre konstruktoru sa v Kotlinu píšu za názov triedy do zátvoriek, rovnako ako keby to boli parametre funkcie alebo metódy:

class Kostka(aPocetSten: Int) {

    val pocetSten: Int

    init {
        pocetSten = aPocetSten
    }
}

Všimnite si, že sme pred názov parametra konstruktoru pridali znak a, pretože inak by mal rovnaký názov ako atribút pocetSten a Kotlin by to zmiatlo. Vráťme sa k Main.kt a zadajte tento parameter do konstruktoru:

fun main(args: Array<String>) {
    val kostka = Kostka(10) // v tuto chvíli se zavolá konstruktor
    println(kostka.pocetSten)
}

výstup:

10

Všetko funguje, ako sme očakávali. Kotlín nám už v tejto chvíli nevygeneruje prázdny (tzv. Bezparametrický konštruktor), takže kocku bez parametra vytvoriť nedá. My to však môžeme umožniť, vytvorme si ďalší konštruktor a tentoraz bez parametra. V ňom zavoláme náš parametrický konštruktor a odovzdáme mu hodnotu 6, pretože takú hodnotu asi užívateľ našej triedy u kocky očakáva ako predvolený:

constructor() : this(6)

Slovíčkom this voláme náš parametrický konštruktor a ešte si ho nižšie podrobnejšie vysvetlíme.

Skúsme si teraz vytvoriť 2 inštancie kocky, každú iným konstruktoru (opäť v Main.kt):

fun main(args: Array<String>) {
    val sestistenna = Kostka()
    val desetistenna = Kostka(10)
    println(sestistenna.pocetSten)
    println(desetistenna.pocetSten)
}

výstup:

6
10

Kotlinu nevadí, že máme 2 konštruktory, pretože ich parametre sú rôzne. Hovoríme o tom, že konštruktor má preťaženia (overload). Toho môžeme využívať aj u metód, nielen u konstruktoru. IntelliJ nám prehľadne ponúka všetky preťaženia konstruktoru alebo metódy vo chvíli, keď zadáme jej názov. V ponuke vidíme naše 2 konštruktory:

Rovnakého správanie by sa dalo docieliť pomocou výchozích argumentů, ale o tom neskôr.

Pomoc k preťaženým konstruktorům - Objektovo orientované programovanie v Kotlin

Mnoho metód v Kotlinu má hneď niekoľko preťaženie, skúste sa pozrieť napr. Na metódu trim() na triede String. Je dobré si u metód prejsť ich preťaženie, aby ste neprogramoval niečo, čo už niekto urobil pred vami.

Ukážeme si ešte, ako ide obísť nepraktický názov atribútu u parametrického konstruktoru (v našom prípade aPocetSten) a potom konstruktory pomaly opustíme. Problém je samozrejme v tom, že keď napíšeme:

class Kostka(pocetSten: Int) {
    init {
        pocetSten = pocetSten
    }
}

Kotlín nevie, ktorú z premenných myslíme, či parameter alebo atribút. V tomto prípade priraďujeme do parametra znova ten istý parameter. Vnútri triedy sa máme možnosť odkazovať na jej inštanciu, je uložená v premennej this. Využitie si môžeme predstaviť napr. Keby kocka mala metódu dejHraci(hrac: Hrac) a tam by volala hrac.seberKostku(this). Tu by sme hráči pomocou this odovzdali seba samého, teda tú konkrétnu kocku, s ktorou pracujeme. My sa tým tu nebudeme zaťažovať, ale využijeme odkazu na inštanciu pri nastavovaní atribútu:

class Kostka(pocetSten: Int) {
    init {
        this.pocetSten = pocetSten
    }
}

Pomocou this sme špecifikovali, že ľavá premenná pocetSten náleží inštanciu, pravú Kotlin chápe ako z parametra. Máme teda 2 konštruktory, ktoré nám umožňujú tvoriť rôzne hracie kocky.

Pretože môže byť trochu zdĺhavé písať triedu s viacerými atribútmi a tieto parametre potom nastavovať pomocou parametrov konstruktoru, umožňuje Kotlin skrátený zápis konstruktoru. Atribút definujeme už v parametri konstruktoru tým, že pridáme kľúčové slovo val alebo var pred jeho názov. Kotlín tým spozná, že chceme vytvoriť atribút triedy s rovnakým názvom ako má parameter konstruktoru. V našom prípade by náš kód vyzeral takto:

class Kostka(val pocetSten: Int) { // Náš atribut jsme definovali již v parametru konstruktoru
    constructor() : this(6)
}

Náhodné čísla

Definujme na kocke metódu hod(), ktorá nám vráti náhodné číslo od 1 do počtu stien. Je to veľmi jednoduché, metóda bude public (pôjde volať zvonku) a nebude mať žiadny parameter. Návratová hodnota bude typu Int. Náhodné číslo získame tak, že si vygenerujeme rozsah s prvkami od 1 do hodnoty premennej pocetSten, zamiešame ho a vyberieme z neho prvý prvok.

Vrátenie zamiešaného rozsahu (rovnako ako pole) prevedieme metódou shuffled(). Rozsah si vytvoríme rovnakým spôsobom ako sme to boli zvyknutí robiť vo for cykloch:

fun hod(): Int {
    return (1..pocetSten).shuffled().first()
}

Prekrývanie metódy toString ()

Kocka je takmer hotová, ukážme si ešte jednu užitočnú metódu, ktorú ju pridáme a ktorú budeme hojne používať aj vo väčšine našich ďalších objektov. Reč je o metóde toString(), o ktorej sme sa už zmienili a ktorú obsahuje každý objekt, teda aj teraz naše kocka. Metóda je určená na to, aby vrátila tzv. Textovú reprezentáciu inštancie. Hodí sa vo všetkých prípadoch, kedy si inštanciu potrebujeme vypísať alebo s ňou pracovať ako s textom. Túto metódu majú napr. Aj čísla.

Už vieme, že v Kotlinu funguje implicitné konverzie, akonáhle teda budeme chcieť do konzoly vypísať objekt, Kotlin na ňom zavolá metódu toString() a vypíše jej výstup. Ak si vytvárame vlastné triedu, mali by sme zvážiť, či sa nám takáto metóda nehodí. Nikdy by sme si nemali vymýšľať vlastnú metódu, napr. Niečo ako vypis(), keď máme v Kotlinu pripravenú cestu, ako toto riešiť. U kocky nemá toString() vyšší zmysel, ale u bojovníka bude určite vracať jeho meno. My si ju ku kocke rovnako pridáme, bude vypisovať, že sa jedná o kocku a vráti aj počet stien. Najprv si skúsme vypísať do konzoly našu inštanciu kocky:

    println(sestistenna)
    println(desetistenna)

Do konzoly sa vypíše iba cesta k našej triede, teda cz.itnetwork.Kostka a tzv. Hash kód objektu. V mojom prípade bol vypísaný tento reťazec:

cz.itnetwork.Kostka@1b6d3586
cz.itnetwork.Kostka@4554617c

Metódu už jednoducho nedefinujeme, ale pretože už existuje, musíme ju prepísať, resp. prekryť. Tým sa opäť nebudeme teraz podrobne zaoberať, však chcem, aby sme už teraz vedeli toString() používať. Pre prekrytie označíme metódu slovíčkom override:

V Kotlinu existuje tzv. Dátové trieda, ktorá nám neskôr vygeneruje túto a ďalšie 2 metódy. Budeme sa o nich baviť v ďalších lekciách.

    override fun toString(): String {
        return "Kostka s $pocetSten stěnami"
    }

Teraz opäť skúsime do konzoly vypísať priamo inštanciu kocky.

výstup:

Kostka s 6 stěnami

Ešte si naše kocky vyskúšame. Skúsime si v programe s našimi dvoma kockami v cykloch hádzať a pozrieme sa, či fungujú tak, ako sa očakáva:

    // vytvoření
    val sestistenna = Kostka()
    val desetistenna = Kostka(10)

    // hod šestistěnnou
    println(sestistenna)
    for (i in 0..9) {
        print(sestistenna.hod().toString() + " ")
    }

    // hod desetistěnnou
    println("\n\n" + desetistenna)
    for (i in 0..9) {
        print(desetistenna.hod().toString() + " ")
    }
class Kostka(pocetSten: Int) {

    val pocetSten: Int

    constructor() : this(6)

    init {
        this.pocetSten = pocetSten
    }

    fun hod(): Int {
        return (1..pocetSten).shuffled().first()
    }

    override fun toString(): String {
        return "Kostka s $pocetSten stěnami"
    }
}

Výstup môže vyzerať nejako takto:

Kostka s 6 stěnami
3 6 6 1 6 3 6 2 6 3

Kostka s 10 stěnami
5 9 9 2 10 4 9 3 10 5

Máme hotovú celkom peknú a nastaviteľnou triedu, ktorá reprezentuje hraciu kocku. Bude sa nám hodiť v našej aréne, ale môžete ju použiť aj kdekoľvek inde. Vidíme, ako OOP umožňuje znovupoužívat komponenty. V budúcej lekcii, Riešené úlohy k 3. lekcii OOP v Kotlin , si vytvoríme bojovníka. :)

V nasledujúcom cvičení, Riešené úlohy k 3. lekcii OOP v Kotlin, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

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é 64x (8.81 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin

 

Predchádzajúci článok
Riešené úlohy k 1.-2. lekciu OOP v Kotlin
Všetky články v sekcii
Objektovo orientované programovanie v Kotlin
Preskočiť článok
(neodporúčame)
Riešené úlohy k 3. lekcii OOP v Kotlin
Článok pre vás napísal Samuel Kodytek
Avatar
Užívateľské hodnotenie:
2 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