Sprievodca po rozhraniach Kotlin

1. Prehľad

V tomto návode prediskutujeme, ako definovať a implementovať rozhrania v Kotline.

Taktiež sa pozrieme na to, ako môže trieda implementovať viac rozhraní. To určite môže spôsobiť konflikty a my sa naučíme mechanizmus, ktorý má Kotlin na ich riešenie.

2. Rozhrania v Kotline

Rozhranie je spôsob poskytovania popisu alebo kontraktu pre triedy v objektovo orientovanom programovaní. Môžu obsahovať vlastnosti a funkcie abstraktným alebo konkrétnym spôsobom v závislosti od programovacieho jazyka. Prejdeme si podrobnosti rozhraní v Kotline.

Rozhrania v Kotline sú podobné rozhraniam v mnohých iných jazykoch, ako je Java. Majú ale konkrétnu syntax, poďme si ich preštudovať v nasledujúcich niekoľkých podkapitolách.

2.1. Definovanie rozhraní

Začnime definovaním nášho prvého rozhrania v Kotline:

rozhranie SimpleInterface

Toto je najjednoduchšie rozhranie, ktoré je úplne prázdne. Sú známe aj ako značkovacie rozhrania.

Poďme teraz do nášho rozhrania pridať niektoré funkcie:

rozhranie SimpleInterface {fun firstMethod (): String fun secondMethod (): String {return ("Hello, World!")}}

Do predtým definovaného rozhrania sme pridali dve metódy:

  • Jeden z nich volal firstMethod je abstraktná metóda
  • Zatiaľ čo ten druhý volal secondMethod má predvolenú implementáciu.

Teraz poďme do nášho rozhrania pridať niektoré vlastnosti:

interface SimpleInterface {val firstProp: String val secondProp: String get () = "Second Property" fun firstMethod (): String fun secondMethod (): String {return ("Hello, from:" + secondProp)}}

Tu sme do nášho rozhrania pridali dve vlastnosti:

  • Jeden z nich sa ozval firstProp je typu String a je abstraktný
  • Druhá sa ozvala druhýProp je tiež typu string, ale definuje implementáciu pre svojho accessor.

Poznač si to vlastnosti v rozhraní nemôžu udržiavať stav. Toto je teda nezákonný výraz v Kotline:

rozhranie SimpleInterface {val firstProp: String = "Prvá vlastnosť" // Neoprávnené vyhlásenie}

2.2. Implementácia rozhraní

Teraz, keď sme definovali základné rozhranie, pozrime sa, ako to môžeme implementovať v triede v Kotline:

class SimpleClass: SimpleInterface {override val firstProp: String = "First Property" override fun firstMethod (): String {return ("Hello, from:" + firstProp)}}

Všimnite si, že keď definujeme SimpleClass ako implementácia SimpleInterface, musíme poskytnúť iba implementáciu pre abstraktné vlastnosti a funkcie. Avšak my môže tiež prepísať ktorúkoľvek predtým definovanú vlastnosť alebo funkciu.

Teraz prepíšeme všetky predtým definované vlastnosti a funkcie v našej triede:

class SimpleClass: SimpleInterface {override val firstProp: String = "First Property" override val secondProp: String get () = "Second Property, Overridden!" prepísať zábavu firstMethod (): String {return ("Hello, from:" + firstProp)} prepísať fun secondMethod (): String {return ("Hello, from:" + secondProp + firstProp)}}

Tu sme prepísali majetok druhýProp a funkcia druhá funkcia ktoré boli predtým definované v rozhraní SimpleInterface.

2.3 Implementácia rozhraní prostredníctvom delegovania

Delegovanie je návrhový vzor v objektovo orientovanom programovaní dosiahnuť opätovnú použiteľnosť kódu zložením namiesto dedičnosti. Aj keď je to možné implementovať v mnohých jazykoch, ako je Java, Kotlin má natívnu podporu pre implementáciu prostredníctvom delegovania.

Ak začneme základným rozhraním a triedou:

rozhranie MyInterface {fun someMethod (): String} trieda MyClass (): MyInterface {prepísať zábavu someMethod (): String {return ("Hello, World!")}}

Zatiaľ nič nové. Teraz však môžeme definovať inú triedu, ktorá sa implementuje MyInterface prostredníctvom delegácie:

trieda MyDerivedClass (myInterface: MyInterface): MyInterface by myInterface

MyDerivedClass očakáva delegáta ako argument, ktorý skutočne implementuje rozhranie MyInterface.

Pozrime sa, ako môžeme zavolať funkciu rozhrania prostredníctvom delegáta:

val myClass = MyClass () MyDerivedClass (myClass) .someMethod ()

Tu sme vytvorili inštanciu Moja trieda a použil to ako delegáta na volanie funkcií rozhrania MyDerivedClass, ktoré tieto funkcie nikdy priamo neimplementovali.

3. Viacnásobné dedičstvo

Viacnásobné dedičstvo je kľúčovým konceptom v paradigme objektovo orientovaného programovania. To umožňuje triede dediť vlastnosti z viac ako jedného nadradeného objektu, napríklad z rozhrania.

Aj keď to poskytuje väčšiu flexibilitu pri modelovaní objektov, prichádza s vlastnou sadou zložitostí. Jedným z nich je „diamantový problém“.

Java 8 má svoje vlastné mechanizmy riešenia problému s diamantmi, rovnako ako akýkoľvek iný jazyk, ktorý umožňuje viacnásobné dedenie.

Pozrime sa, ako to Kotlin rieši prostredníctvom rozhraní.

3.1. Dedenie viacerých rozhraní

Začneme definovaním dvoch jednoduchých rozhraní:

interface FirstInterface {fun someMethod (): String fun anotherMethod (): String {return ("Hello, from anotherMethod in FirstInterface")}} interface SecondInterface {fun someMethod (): String {return ("Hello, from someMethod in SecondInterface") } fun anotherMethod (): String {return ("Hello, from anotherMethod in SecondInterface")}}

Upozorňujeme, že obe rozhrania majú metódy s rovnakou zmluvou.

Teraz definujme triedu, ktorá dedí z oboch týchto rozhraní:

trieda SomeClass: FirstInterface, SecondInterface {prepísať zábavu someMethod (): String {return ("Hello, from someMethod in SomeClass")} override fun anotherMethod (): String {return ("Hello, from anotherMethod in SomeClass")}}

Ako vidíme, SomeClass realizuje oboje Prvé rozhranie a Druhé rozhranie. Aj keď je to syntakticky celkom jednoduché, je tu treba venovať pozornosť trochu sémantiky. Prejdeme si to v ďalšej podkapitole.

3.2. Riešenie konfliktov

Pri implementácii viacerých rozhraní môže trieda zdediť funkciu, ktorá má predvolenú implementáciu pre tú istú zmluvu vo viacerých rozhraniach. To vyvoláva problém vyvolania tejto funkcie z inštancie implementujúcej triedy.

Na vyriešenie tohto konfliktu vyžaduje Kotlin podtriedu, aby pre tieto funkcie poskytla prepísanú implementáciu, aby bolo rozlíšenie explicitné.

Napríklad, SomeClass vyššie realizuje iná metóda. Keby to však nebolo, Kotlin by nevedel, či sa má dovolať najprv alebo SecondInterface predvolená implementácia iná metóda. SomeClass musí realizovať iná metóda pre tento dôvod.

Avšak someMethod je trochu iná, pretože v skutočnosti neexistuje žiadny konflikt. Prvé rozhranie neposkytuje predvolenú implementáciu pre someMethod. To znamená, SomeClass stále to musí implementovať, pretože Kotlin nás núti implementovať všetky zdedené funkcie, bez ohľadu na to, či sú definované raz alebo viackrát v nadradených rozhraniach.

3.3. Riešenie diamantového problému

„Diamantový problém“ nastane, keď dva podradené objekty základného objektu popisujú konkrétne správanie definované základným objektom. Teraz musí objekt dediaci z oboch týchto podradených objektov vyriešiť, ktoré zdedené správanie sa prihlasuje.

Kotlinovo riešenie tohto problému spočíva v pravidlách definovaných pre viacnásobné dedičstvo v predchádzajúcej pododdiele. Poďme definovať niekoľko rozhraní a implementačnú triedu na predstavenie diamantového problému:

interface BaseInterface {fun someMethod (): String} interface FirstChildInterface: BaseInterface {override fun someMethod (): String {return ("Hello, from someMethod in FirstChildInterface")}} interface SecondChildInterface: BaseInterface {override fun someMethod (): String {return ("Hello, from someMethod in SecondChildInterface")}} class ChildClass: FirstChildInterface, SecondChildInterface {override fun someMethod (): String {return super.someMethod ()}}

Tu sme definovali BaseInterface ktorá deklarovala abstraktnú funkciu zvanú someMethod. Obidve rozhrania FirstChildInterface a SecondChildInterface dedí z BaseInterface a implementovať funkciu someMethod.

Teraz, keď implementujeme ChildClass dediť z FirstChildInterface a SecondChildInterface, je potrebné, aby sme funkciu prepísali someMethod. Avšak aj keď musíme metódu prepísať, stále môžeme jednoducho zavolať Super ako robíme tu s SecondChildInterface.

4. Rozhrania v porovnaní s abstraktnými triedami v Kotline

Abstraktné kurzy v Kotline sú triedy, ktoré nie je možné vytvoriť inštanciou. Môže obsahovať jednu alebo viac vlastností a funkcií. Tieto vlastnosti a funkcie môžu byť abstraktné alebo konkrétne. Akákoľvek trieda dediaca z abstraktnej triedy musí implementovať všetky zdedené abstraktné vlastnosti a funkcie, pokiaľ samotná táto trieda nie je tiež deklarovaná ako abstraktná.

4.1. Rozdiely medzi rozhraním a abstraktnou triedou

Počkaj! Neznie to presne tak, ako to robí rozhranie?

V skutočnosti sa abstraktná trieda na začiatku veľmi nelíši od rozhrania. Existujú však jemné rozdiely, ktoré ovplyvňujú výber, ktorý urobíme:

  • Trieda v Kotline môže implementovať toľko rozhraní, koľko sa im páči, ale môže siahať iba od jednej abstraktnej triedy
  • Vlastnosti v rozhraní nemôžu udržiavať stav, zatiaľ čo v abstraktnej triede

4.2. Kedy by sme mali čo použiť?

Rozhranie je iba vzorom na definovanie tried, môžu mať voliteľne tiež niektoré predvolené implementácie. Na druhej strane je abstraktná trieda neúplná implementácia, ktorú dokončujú rozširujúce triedy.

Typicky Na definovanie zmluvy by sa mali použiť rozhrania, ktorá vyvoláva schopnosti, ktoré sľubuje poskytnúť. Zodpovednosť za splnenie týchto sľubov nesie implementačná trieda. An abstraktná trieda by sa však mala používať na zdieľanie čiastkových charakteristík s rozširujúcimi sa triedami. Rozširujúca sa trieda môže trvať ďalšie dokončenie.

5. Porovnanie s rozhraním Java

So zmenami rozhrania Java v prostredí Java 8 veľmi sa priblížili k Kotlinovým rozhraniam. Jeden z našich predchádzajúcich článkov zachytáva nové funkcie zavedené v prostredí Java 8 vrátane zmien v rozhraní.

Teraz existujú zväčša syntaktické rozdiely medzi rozhraniami Java a Kotlin. Jeden vynikajúci rozdiel súvisí s kľúčovým slovom „prepísať“. V Kotline je pri implementácii abstraktných vlastností alebo funkcií zdedených z rozhrania povinné ich kvalifikovať kľúčovým slovom „prepísať„. V Jave takáto výslovná požiadavka neexistuje.

6. Záver

V tomto tutoriáli sme diskutovali o rozhraniach Kotlin, ako ich definovať a implementovať. Potom sme hovorili o dedení z viacerých rozhraní a konflikte, ktorý môžu vytvárať. Pozreli sme sa na to, ako Kotlin takéto konflikty rieši.

Nakoniec sme diskutovali o rozhraniach v porovnaní s abstraktnými triedami v Kotline. Krátko sme hovorili aj o tom, ako je porovnané rozhranie Kotlin s rozhraním Java.

Ako vždy, kód príkladov je k dispozícii na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found