16. diel - Rozhranie (interface) v Kotlin
V predchádzajúcom cvičení, Riešené úlohy k 10.-15. lekciu OOP v Kotlin, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Riešené úlohy k 10.-15. lekciu OOP v Kotlin , sme si precvičili prácu s kolekciou
List
pri tvorbe elektronického diára. Dnes to bude opäť trochu
teoretické, objavíme ďalšie topí objektovo orientovaného programovania,
uvedieme si totiž rozhrania.
Rozhranie
Rozhraním objektu sa myslí to, ako je objekt viditeľný zvonku. Už vieme,
že objekt obsahuje nejaké metódy, tie môžu byť privátne alebo verejné.
Rozhranie objektu tvorí práve jeho verejné metódy, je to spôsob, akým s
určitým typom objektu môžeme komunikovať. Už sme niekoľkokrát mohli
vidieť aké verejné metódy naša trieda ponúka, napr. U nášho bojovníka
do arény. Trieda Bojovnik
mala nasledujúce verejné metódy:
utoc(souper: Bojovnik)
branSe(uder: Int)
nazivu(): Boolean
nastavZpravu(zprava: String)
vratPosledniZpravu(): String
grafickyZivot(): String
Pokiaľ si do nejakej premennej uložíme inštanciu bojovníka, môžeme na
ňu volať metódy ako utoc()
alebo branSe()
. To
stále nie je nič nové, že?
My si však rozhrania môžeme deklarovať zvlášť a to podobným spôsobom ako napríklad triedu. Toto rozhranie potom použijeme ako dátový typ.
Všetko si vyskúšame, ale na niečom jednoduchším, než je bojovník.
Vytvorme si nový projekt a nazvime ho Rozhrani
. Pridáme si
nejakú jednoduchú triedu. Pretože by sa podľa môjho názoru mala teórie
vysvetľovať na niečom odľahčujúcim, urobíme si Vtáka. Bude vedieť
pípať, dýchať a jednoduchú recykláciu. Pridajme si triedu
Ptak
, bude vyzerať takto:
class Ptak { fun pipni() { println("♫ ♫ ♫") } fun dychej() { println("Dýchám...") } fun klovni() { println("Klov, klov!") } }
Trieda je naozaj triviálne. Prejdime do Main.kt
a vytvorme si
inštanciu vtáka:
val ptak = Ptak()
Teraz napíšeme ptak.
a necháme IntelliJ, aby nám zobrazil
metódy na triede (možno tiež vyvolať stlačením Ctrl +
Space):
Vidíme, čo na vtákovi môžeme všetko volať. Sú tam samozrejme tie 3 metódy, čo sme v triede implementovali (plus ďalšie, ktoré majú objekty v základe).
Teraz vtákovi vytvoríme rozhranie. Využijeme na to kľúčového slova
interface
(anglicky rozhranie). Pomenovanie
rozhranie v Kotlinu je pomerne veda (rovnako ako v Jave). My
sa uspokojíme s názvom PtakInterface
. Pravým tlačidlom klikneme
na projekt a pridáme "New" -> "Kotlin File / Class". V dropdown menu
vyberieme Interface.
K projektu sa nám pridá prázdny interface. Do neho pridáme hlavičky metód, ktoré má dané rozhranie obsahovať. Samotnú implementáciu (kód metód) uvedieme až v triede, ktorá bude toto rozhranie implementovať (viď ďalej).
Kotlín, rovnako ako Java od verzie 8, umožňuje
uvádzať v rozhraní i tela metód, teda ich kód. To je proti tomu, čo si tu
dnes budeme vysvetľovať, a táto funkcionalita vznikla z dôvodu podpory tzv.
Trait. S tými sa pracuje inak ako s rozhraním a jazyky ako sú napr. PHP alebo
Scala sú na túto funkcionalitu vlastné kľúčové slovo
trait
.
Do rozhrania PtakInterface
teda pridáme hlavičky metód,
schválne jednu vynecháme a pridáme iba Pípanie a Dýchanie:
interface PtakInterface { fun pipni() fun dychej() }
Rozhranie obsahuje vždy len verejné metódy (inak by nemalo zmysel, udáva, ako s objektom zvonku pracovať).
Vráťme sa do Main.kt
a zmeňme riadok s premennou
ptak
tak, aby už nebola typu Ptak
, ale
PtakInterface
:
val ptak: PtakInterface = Ptak()
Kódom vyššie hovoríme, že v premennej typu PtakInterface
očakávame objekt, ktorý poskytuje tie metódy, čo sú v rozhraní. Kotlín
nám vynadá, pretože trieda Ptak
zatiaľ rozhranie
PtakInterface
neimplementuje, aj keď potrebné metódy má, nevie,
že rozhranie poskytuje. Presunieme sa do triedy Ptak
a nastavíme
jej, že implementuje interface PtakInterface
. Implementovaným
metódam dodáme kľúčové slovo override
(rovnako ako u
dedičnosti). Interface sa implementuje operátorom :
:
class Ptak: PtakInterface { override fun pipni() { println("♫ ♫ ♫") } override fun dychej() { println("Dýchám...") } fun klovni() { println("Klov, klov!") } }
Keď sa teraz vrátime do Main.kt
, riadok s premennou typu
PtakInterface
je už v poriadku. Trieda Ptak
korektne
implementuje rozhranie PtakInterface
a jej inštancie môže byť
do premennej tohto typu uložená.
Skúsme teraz vymazať z triedy nejakú metódu, ktorú rozhranie udáva,
napr. pipni()
. IntelliJ nás upozorní, že implementácia nie je
kompletný. Vráťme ju zas späť.
Opäť pridáme riadok ptak.
, IntelliJ nám ponúkne
nasledujúce metódy:
Vidíme, že na inštanciu môžeme teraz volať iba metódy, ktoré
poskytuje rozhranie. To preto, že premenná ptak
je už typu
PtakInterface
, nie Ptak
. Metóda klovni()
úplne chýba.
K čomu je to dobré? Výhod a využitie je viac, na prvý sme už prišli. Pomocou rozhrania dokážeme zjednodušiť rozhranie nejakého zložitého objektu a vystaviť len tú časť, ktorá sa nám v tú dobu hodí.
Ešte dodám, že nemôžeme vytvoriť inštanciu z rozhrania, tento kód nebude fungovať:
// tento kód nebude fungovat val ptak: PtakInterface = PtakInterface()
Viacnásobná dedičnosť
Kotlín (rovnako ako väčšina jazykov) nepodporuje viacnásobnú dedičnosť. Nemôžeme teda jednu triedu oddědit z niekoľkých iných tried. Je to hlavne z toho dôvodu, že môže vzniknúť problém kolízie názvov metód v rôznych triedach, z ktorých dedíme (Diamond problem). Viacnásobná dedičnosť sa často obchádza práve cez interface, pretože tých môžeme v triede implementovať koľko chceme. Umožňuje nám to s inštanciou potom pracovať určitým spôsobom a vôbec nás nezaujíma, akého typu objekt v skutočnosti je a čo všetko navyše obsahuje.
Pridajme si k projektu interface
JesterInterface
. Bude to interface jaštera. Ten
bude vedieť tiež dýchať a ešte sa plaziť:
interface JesterInterface { fun plazSe() fun dychej() }
Vyskúšajme si "viacnásobnú dedičnosť", presnejšie implementáciu viac
rozhraní v jednej triede. Urobme si ptakoještěr. Pridajme k projektu triedu
PtakoJester
. Bude implementovať rozhranie
PtakInterface
a JesterInterface
:
class PtakoJester: JesterInterface, PtakInterface {
}
Keď teraz klikneme na ikonu žiarovky, môžeme v kontextovom menu zvoliť možnosť "Implement members". IntelliJ nám automaticky do triedy vygeneruje potrebné metódy.
Po implementácii oboch rozhranie vyzerá kód triedy takto:
class PtakoJester: JesterInterface, PtakInterface { override fun pipni() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun plazSe() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun dychej() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }
Metódy v Jester doimplementujeme:
override fun pipni() { println("♫ ♫♫ ♫ ♫ ♫♫") } override fun plazSe() { println("Plazím se...") } override fun dychej() { println("Dýchám...") }
Presuňme sa do Main.kt
a vytvorme si inštanciu
ptakoještěr:
val ptakoJester = PtakoJester()
Uistime sa, že má metódy ako vtáka, tak jaštera:
V budúcej lekcii, Pretypovania a hierarchie objektov v Kotlin , budeme pokračovať v podobnom duchu. Rozhranie ešte neopustíme a naučíme sa ďalšie pokročilé techniky objektovo orientovaného programovania.
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é 24x (23.18 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin