Preťaženie operátora v Kotline

1. Prehľad

V tomto výučbe si povieme o konvenciách, ktoré poskytuje Kotlin na podporu preťaženia operátorov.

2. The operátor Kľúčové slovo

V Jave sú operátori viazaní na konkrétne typy Javy. Napríklad, String a číselné typy v Jave môžu používať operátor + na zreťazenie a sčítanie. Žiadny iný typ Java nemôže tohto operátora znova použiť pre svoje vlastné výhody. Kotlin naopak poskytuje súbor dohovorov na podporu obmedzených Preťaženie operátora.

Začnime jednoduchým dátová trieda:

dátová trieda Bod (val x: Int, val y: Int)

Túto triedu údajov vylepšíme o niekoľko operátorov.

Aby sa z funkcie Kotlin s preddefinovaným názvom stal operátor, mali by sme funkciu označiť operátor modifikátor. Napríklad môžeme preťažiť “+” operátor:

zábava pre operátora Point.plus (ďalšie: Point) = Point (x + other.x, y + other.y)

Takto môžeme pridať dve Body s “+”:

>> val p1 = Bod (0, 1) >> val p2 = Bod (1, 2) >> println (p1 + p2) Bod (x = 1, y = 3)

3. Preťaženie pre unárne operácie

Unárne operácie sú tie, ktoré pracujú iba s jedným operandom. Napríklad, -a, ++ alebo ! a sú unárne operácie. Všeobecne platí, že funkcie, ktoré idú preťažiť unárne operátory, neberú žiadne parametre.

3.1. Unárnik Plus

Čo tak postaviť a Tvar nejakého druhu s niekoľkými Body:

val s = tvar {+ Bod (0, 0) + Bod (1, 1) + Bod (2, 2) + Bod (3, 4)}

V Kotline je to úplne možné s unaryPlus funkcia operátora.

Keďže a Tvar je len zbierka Body, potom môžeme napísať triedu, ktorá obsahuje niekoľko Bods možnosťou pridania ďalších:

trieda Tvar {private val points = mutableListOf () operator fun Point.unaryPlus () {points.add (this)}}

A všimnite si, že čo nám dalo tvar {…} syntax mala používať a Lambda s Prijímače:

zábavný tvar (init: Tvar. () -> Jednotka): Tvar {tvar valca = Tvar () tvar.init () vratny tvar}

3.2. Unárne mínus

Predpokladajme, že máme Bod menovaný „P“ a budeme negovať jeho koordináciu pomocou niečoho ako „-P“. Potom musíme iba definovať funkciu operátora s názvom unaryMinus na Bod:

operátor zábava Point.unaryMinus () = bod (-x, -y)

Potom zakaždým, keď pridáme a “-“ predpona pred inštanciou Bod, prekladač to preloží do a unaryMinus volanie funkcie:

>> val p = Bod (4, 2) >> println (-p) Bod (x = -4, y = -2)

3.3. Prírastok

Každú súradnicu môžeme zvýšiť o jednu iba implementáciou funkcie operátora s názvom :

zábava pre operátora Point.inc () = bod (x + 1, y + 1)

Postfix “++” operátor, vráti najskôr aktuálnu hodnotu a potom ju zvýši o jednu:

>> var p = Bod (4, 2) >> println (p ++) >> println (p) Bod (x = 4, y = 2) Bod (x = 5, y = 3)

Naopak, predpona “++” operátor, najskôr zvýši hodnotu a potom vráti novo zvýšenú hodnotu:

>> println (++ p) bod (x = 6, y = 4)

Tiež od “++” operátor znovu priradí použitú premennú, nemôžeme ju použiť val s nimi.

3.4. Zníženie

Docela podobné prírastku, môžeme znížiť každú súradnicu implementáciou dec funkcia operátora:

operátor zábava Point.dec () = Bod (x - 1, y - 1)

dec podporuje tiež známu sémantiku pre operátory pred a po znížení ako pre bežné číselné typy:

>> var p = Bod (4, 2) >> println (p--) >> println (p) >> println (- p) Bod (x = 4, y = 2) Bod (x = 3, y) = 1) Bod (x = 2, y = 0)

Tiež ako ++ nemôžeme použiť s vals.

3.5. Nie

Čo tak prevrátiť súradnice iba o ! str? Môžeme to urobiť pomocou nie:

operátor zábava Point.not () = Bod (y, x)

Jednoducho povedané, prekladač preloží akýkoľvek "! P" na volanie funkcie do „Nie“ unárna funkcia operátora:

>> val p = Bod (4, 2) >> println (! p) Bod (x = 2, y = 4)

4. Preťaženie pre binárne operácie

Binárne operátory, ako naznačuje ich názov, sú tie, ktoré pracujú na dvoch operandoch. Funkcie preťažujúce binárne operátory by teda mali prijať aspoň jeden argument.

Začnime s aritmetickými operátormi.

4.1. Plus aritmetický operátor

Ako sme videli skôr, v Kotline môžeme preťažiť základné matematické operátory. Môžeme použiť “+” pridať dve Body spolu:

zábava pre operátora Point.plus (iné: Point): Point = Point (x + other.x, y + other.y)

Potom môžeme napísať:

>> val p1 = Bod (1, 2) >> val p2 = Bod (2, 3) >> println (p1 + p2) Bod (x = 3, y = 5)

Odkedy plus je funkcia binárneho operátora, mali by sme deklarovať parameter funkcie.

Väčšina z nás teraz zažila nedovolené spájanie dvoch BigIntegers:

BigInteger zero = BigInteger.ZERO; BigInteger one = BigInteger.ONE; one = one.add (nula);

Ako sa ukázalo, existuje lepší spôsob, ako pridať dve BigIntegers v Kotline:

>> val one = BigInteger.ONE println (one + one)

Toto funguje, pretože Samotná štandardná knižnica Kotlin sama pridáva svoj spravodlivý podiel operátorov rozšírení na vstavaných typoch ako BigInteger.

4.2. Ostatní aritmetickí operátori

Podobný plus, odčítanie, násobenie, divízia, a zvyšok fungujú rovnako:

zábava pre operátora Point.minus (iné: Point): Point = bod (x - other.x, y - other.y) zábava pre operátora Point.times (iné: Point): Point = Point (x * other.x, y * other.y) operator fun Point.div (other: Point): Point = Point (x / other.x, y / other.y) operator fun Point.rem (other: Point): Point = Point (x% other.) x, y% ostatné.y)

Potom prekladá Kotlin akékoľvek volanie “-“, “*”, „/“ Alebo „%“ do „Mínus“, „Časy“, „Div“ alebo „rem“ , v uvedenom poradí:

>> val p1 = Bod (2, 4) >> val p2 = Bod (1, 4) >> println (p1 - p2) >> println (p1 * p2) >> println (p1 / p2) Bod (x = 1, y = 0) Bod (x = 2, y = 16) Bod (x = 2, y = 1)

Alebo čo tak škálovať a Bod číselným faktorom:

zábava pre operátora Point.times (faktor: Int): Point = Point (x * factor, y * factor)

Takto môžeme napísať niečo ako „P1 * 2“:

>> val p1 = Bod (1, 2) >> println (p1 * 2) Bod (x = 2, y = 4)

Ako vidíme z predchádzajúceho príkladu, pre dva operandy neexistuje povinnosť byť rovnakého typu. To isté platí pre návratové typy.

4.3. Komutativita

Preťažení operátori nie sú vždy komutatívny. To znamená, nemôžeme vymeniť operandy a očakávať, že veci budú fungovať čo najhladšie.

Napríklad môžeme škálovať a Bod integrálnym faktorom jeho vynásobením na Int, povedať „P1 * 2“, ale nie naopak.

Dobrou správou je, že môžeme definovať funkcie operátora na vstavaných typoch Kotlin alebo Java. Aby bolo možné „2 * p1“ prácu, môžeme definovať operátora na Int:

zábava pre operátora Int. časy (bod: Bod): Bod = Bod (point.x * this, point.y * this)

Teraz môžeme s radosťou používať „2 * p1“ tiež:

>> val p1 = Bod (1, 2) >> println (2 * p1) Bod (x = 2, y = 4)

4.4. Zložené úlohy

Teraz, keď môžeme pridať dve BigIntegers s “+” operátor, môžeme byť schopní použiť zložené priradenie pre “+” ktorý je “+=”. Skúsme tento nápad:

var one = BigInteger.ONE one + = one

Štandardne, keď implementujeme jeden z aritmetických operátorov, povedzme "plus", Kotlin podporuje nielen známe “+” operátor, to isté robí aj pre zodpovedajúce zložené zadanie, čo je „+ =“.

To znamená, že bez ďalšej práce môžeme urobiť aj:

var bod = Bod (0, 0) bod + = Bod (2, 2) bod - = Bod (1, 1) bod * = Bod (2, 2) bod / = Bod (1, 1) bod / = Bod ( 2, 2) bod * = 2

Niekedy však toto predvolené správanie nie je to, čo hľadáme. Predpokladajme, že použijeme “+=” pridať prvok do a MutableCollection.

Pre tieto scenáre to môžeme explicitne uviesť implementáciou funkcie operátora s názvom plusAssign:

operator fun MutableCollection.plusAssign (element: T) {add (element)}

Pre každý aritmetický operátor existuje zodpovedajúci operátor zloženého priradenia, ktorý má všetky "Priradiť" prípona. To znamená, že existujú plusAssign, mínusAssign, timesAssign, divAssign, a remAssign:

>> val farby = mutableListOf ("červená", "modrá") >> farby + = "zelená" >> println (farby) [červená, modrá, zelená]

Musia sa vrátiť všetky funkcie operátora zloženého priradenia Jednotka.

4.5. Dohovor o rovnakom zaobchádzaní

Ak prepíšeme rovná sa metódou, potom môžeme použiť “==” a “!=” operátorov, tiež:

trieda Peniaze (suma čiastky: BigDecimal, mena valuty: Mena): Porovnateľné {// vynechané prepísanie zábava sa rovná (iné: Akékoľvek?): Boolean {if (this === other) return true if (other! is Money) return false if (amount! = other.amount) return false if (currency! = other.currency) return false return true} // Rovná sa kompatibilná implementácia hashcode} 

Kotlin prekladá akékoľvek hovory “==” a “!=” operátorov do rovná sa volanie funkcie, samozrejme za účelom vykonania “!=” práce, výsledok volania funkcie sa invertuje. Upozorňujeme, že v tomto prípade nepotrebujeme operátor kľúčové slovo.

4.6. Prevádzkovatelia porovnania

Je čas sa báť BigInteger ešte raz!

Predpokladajme, že spustíme podmienenú logiku, ak existuje BigInteger je väčší ako ten druhý. V prostredí Java nie je riešenie také čisté:

if (BigInteger.ONE.compareTo (BigInteger.ZERO)> 0) {// nejaká logika}

Pri použití toho istého BigInteger v Kotline to môžeme čarovne napísať:

if (BigInteger.ONE> BigInteger.ZERO) {// rovnaká logika}

Táto mágia je možná, pretože Kotlin má špeciálne zaobchádzanie s Java Porovnateľné.

Zjednodušene môžeme nazvať porovnať s metóda v Porovnateľné rozhranie niekoľkými Kotlinovými konvenciami. Akékoľvek porovnania vykonané „<“, “”, alebo “>=” bude preložené do a porovnať s volanie funkcie.

Aby sme mohli použiť operátory porovnania na type Kotlin, musíme implementovať ich Porovnateľné rozhranie:

trieda Peniaze (suma čiastky: BigDecimal, mena valuty: Mena): Porovnateľná {override fun compareTo (other: Money): Int = convert (Currency.DOLLARS) .compareTo (other.convert (Currency.DOLLARS)) zábavná konverzia (mena: Mena): BigDecimal = // vynechané}

Potom môžeme porovnávať peňažné hodnoty tak jednoduché ako:

val oneDollar = peniaze (BigDecimal.ONE, Currency.DOLLARS) val tenDollars = peniaze (BigDecimal.TEN, Currency.DOLLARS) if (oneDollar <tenDollars) {// vynechané}

Keďže porovnať s funkcia v Porovnateľné rozhranie je už označené symbolom operátor modifikátor, nemusíme ho pridávať sami.

4.7. V dohovore

Za účelom kontroly, či prvok patrí do a Strana, môžeme použiť „V“ dohovor:

zábava pre operátora Page.contains (element: T): Boolean = element in elements ()

Opäť prekladač by preložil „V“ a "! V" konvencie na volanie funkcie do obsahuje funkcia operátora:

>> val page = firstPageOfSomething () >> „This“ in page >> „That“! in page

Objekt na ľavej strane „V“ bude odovzdaný ako argument obsahuje a obsahuje funkcia by bola volaná na operande na pravej strane.

4.8. Získajte indexer

Indexátory umožňujú inštancie typu indexovať rovnako ako polia alebo kolekcie. Predpokladajme, že vymodelujeme stránkovanú kolekciu prvkov ako Strana, nehanebne vytrhávajúc nápad z Spring Data:

stránka rozhrania {fun pageNumber (): Int fun pageSize (): Int fun elements (): MutableList}

Normálne, aby sa získal prvok z a Stránka, mali by sme najskôr zavolať prvkov funkcia:

>> val page = firstPageOfSomething () >> page.elements () [0]

Keďže Strana sám o sebe je len efektným obalom pre inú kolekciu, môžeme na vylepšenie jeho API použiť operátory indexovania:

zábava pre operátora Page.get (index: Int): T = elements () [index]

Kompilátor Kotlin nahrádza ktorýkoľvek stránka [index] na a Strana do a získať (index) volanie funkcie:

>> val page = firstPageOfSomething () >> strana [0]

Môžeme ísť ešte ďalej a pridať toľko argumentov, koľko chceme dostať deklarácia metódy.

Predpokladajme, že získame časť zabalenej zbierky:

zábava pre operátora Page.get (start: Int, endExclusive: Int): List = elements (). subList (start, endExclusive)

Potom môžeme nakrájať a Strana Páči sa mi to:

>> val page = firstPageOfSomething () >> page [0, 3]

Tiež môžeme použiť akékoľvek typy parametrov pre dostať operátorská funkcia, nielen Int.

4.9. Nastaviť indexer

Okrem použitia indexátorov na implementáciu get-like sémantika, môžeme ich použiť na napodobnenie operácií podobných množinám, tiež. Všetko, čo musíme urobiť, je definovať pomenovanú operátorskú funkciu nastaviť s najmenej dvoma argumentmi:

zábava pre operátora Page.set (index: Int, hodnota: T) {elements () [index] = hodnota}

Keď vyhlásime a nastaviť Funkcia s iba dvoma argumentmi, prvý z nich by sa mal použiť v zátvorke a ďalší za znakom zadanie:

val page: Page = firstPageOfSomething () page [2] = "Niečo nové"

The nastaviť funkcia môže mať aj viac ako len dva argumenty. Ak je to tak, posledným parametrom je hodnota a zvyšok argumentov by sa mal uviesť v zátvorkách.

4.10. Vzývať

V Kotline a mnohých ďalších programovacích jazykoch je možné vyvolať funkciu pomocou functionName (args) syntax. Je tiež možné napodobniť syntax volania funkcie pomocou znaku vzývať funkcie operátora. Napríklad za účelom použitia stránka (0) namiesto stránka [0] pre prístup k prvému prvku môžeme deklarovať rozšírenie:

zábava pre operátora Page.invoke (index: Int): T = elements () [index]

Potom môžeme na získanie konkrétneho prvku stránky použiť nasledujúci prístup:

assertEquals (strana (1), „Kotlin“)

Tu Kotlin preloží zátvorky na volanie do vzývať metóda s príslušným počtom argumentov. Ďalej môžeme vyhlásiť vzývať operátor s ľubovoľným počtom argumentov.

4.11. Dohovor o iterátore

Čo tak iterácia a Strana ako iné zbierky? Musíme len deklarovať operátorovú funkciu s názvom iterátor s Iterátor ako návratový typ:

zábava pre operátora Page.iterator () = elements (). iterator ()

Potom môžeme iterovať cez a Strana:

val page = firstPageOfSomething () for (e in page) {// Urobte niečo s každým prvkom}

4.12. Dohovor o dosahu

V Kotline môžeme vytvoriť rozsah pomocou “..” operátor. Napríklad, “1..42” vytvára rozsah s číslami od 1 do 42.

Niekedy je rozumné použiť operátor rozsahu na iných nečíselných typoch. Štandardná knižnica Kotlin poskytuje rangeTo dohovor o všetkých Porovnateľné:

operátorská zábava  T.rangeTo (that: T): ClosedRange = ComparableRange (this, that)

Môžeme to použiť na získanie niekoľkých po sebe nasledujúcich dní ako rozsahu:

val now = LocalDate.now () val days = now..now.plusDays (42)

Rovnako ako u iných operátorov, kompilátor Kotlin nahrádza akýkoľvek “..” s rangeTo volanie funkcie.

5. Používajte operátorov uvážlivo

Preťaženie operátora je v Kotline výkonnou funkciou čo nám umožňuje písať stručnejšie a niekedy čitateľnejšie kódy. S veľkou mocou však prichádza aj veľká zodpovednosť.

Preťaženie operátora môže spôsobiť, že náš kód bude mätúci alebo dokonca ťažko čitateľný keď je príliš často používaný alebo občas zneužitý.

Pred pridaním nového operátora k určitému typu sa teda najskôr opýtajte, či je operátor sémanticky vhodný na to, čo sa snažíme dosiahnuť. Alebo sa opýtajte, či môžeme dosiahnuť rovnaký efekt normálnymi a menej magickými abstrakciami.

6. Záver

V tomto článku sme sa dozvedeli viac o mechanike preťaženia operátora v Kotline a o tom, ako na jeho dosiahnutie využíva súbor konvencií.

Implementáciu všetkých týchto príkladov a útržkov kódu nájdete v projekte GitHub.


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