Abych Kotlin podrobil dostatečné zkoušce, nemohlo to samozřejmě zůstat jen u přepisu jednoduché Spring Boot aplikace. Rozhodl jsem se tedy, že dostatečným kandidátem by mohla být IRC brána ChatCzGate, kterou jsme s Imrijou napsali kdysi dávno v Pythonu. V tomto článku bych se chtěl podělit o pár postřehů, ale i nějaké to zamyšlení ohledně toho, co se v “Kotlinovském kruhu” odehrává.

Screenshot vydá víc jak 1000 slov

Asi tou nejkrásnější věcí na Kotlinu je pro mě null-safety, což považuji za opravdovou přidanou hodnotu, když ho srovnám se Scalou. Nejen tedy, že překladač okamžitě řve, kdykoliv se pokusím dereferencovat potenciální null pointer, ale onen ?. safe-call zápis je zkrátka roztomiloučký 🙂 . Tedy to, co níže vidíte v červeném rámečku znamená, že forEach() se vykoná pouze tehdy, pokud room i users nebude null. Samozřejmě, mohl bych si tam 2x udělat if (room != null) …, ale s tím otazníkem mi to přijde prostě elegantnější. Dále je hezké, že IDEA zvýrazňuje mírně zelenou barvou tzv. smart-casty. Ty najdou uplatnění nejen při instanceof (které je v Kotlinu zkráceno jen na “is”), ale i zde, kde room, jak mi ho chatApi vrací, je typu Room? a kompilátor je natolik chytrý, že jelikož je naprosto jisté, že uvnitř forEach() konstanta room null nikdy nebude, automaticky mi jí přetypuje z Room? na Room.

Krom Kotlinu zde můžete vidět v akci i Rojo 🙂 . Vytvořil jsem si RojoMatcher (firstNonBlank), protože se zdá, že se mi bude hodit vícekrát. Akorát se dívám, že by bylo lepší, kdyby se v tomto případě metoda .find() jmenovala .findIn() – dávalo by to větší smysl (uvidíme, možná upravím v další verzi). Ještě byste mi mohli vytknout, že bych mohl pořadí argumentů u sendWhoUserInfo a setUserMode trochu konsolidovat, ať to není jednou tak a podruhé onak 🙂 (opravím, všechno bude).

Mimo výše zmíněného a na screenshotu zvýrazněného jste si ještě možná všimli metody .fromWhitespace(). Jelikož IRC klient neumožňuje, aby názvy kanálu obsahovaly mezeru (avšak Chat.cz ano), musel jsem to trochu ošulit. A tak jsem si říkal, že by pro tento účel bylo fajn, kdyby běžný datový typ String měl navíc metody toWhitespace() a fromWhitespace(). Voilà:

package com.svetylkovo.chatczgate.extensions

private val UNICODE_SPACE = "\u00A0"

fun String.toWhitespace() = this.replace(" ", UNICODE_SPACE)

fun String.fromWhitespace() = this.replace(UNICODE_SPACE, " ")

… a je vystaráno. Jen pozor na to, extensions můžou být dobrý sluha, ale i zlý pán. Osobně si myslím, že by se měly používat jen pokud je to vyloženě nutné/žádoucí a zároveň se snažit jejich scope omezit pokud možno co nejvíce. Příliš mnoho extensions, dle mého názoru, můžou docela ubrat na čitelnosti kódu, protože až to po vás bude číst někdo jiný, nebude mít tucha, co to jsou za volání a kde se tam vůbec vzaly (bude ho to stát úsilí si to zjistit).

K čemu jsou Backing Fields

Během svého přepisu nadešlo i na to, že jsem byl nucen napsat si svůj první custom getter pro jednu property a hned jsem narazil na problém. Na první pokus jsem jí napsal takto:

    var userName = ""
    var nick = ""
        get() = if (nick.isEmpty()) userName else nick

Problém nastal proto, že kdykoliv napíšu “nick”, tak se opravdu ve skutečnosti volá getNick() a pokud tohle zavolám uvnitř getteru (resp. uvnitř getNick()), tak dostanu nekonečný cyklus a přeteče mi stack 🙂 . Jak z toho ven? Až nyní jsem na vlastní kůži pochopil, proč má Kotlin pro tento účel slůvko “field” (které IDEA zvýrazní krásnou černou barvou). Tímto jsem schopný odkazovat se přímo na daný field (v tomto případě “nick”) aniž bych přímo volal jeho getter. Takže správná a funkční verze je:

    var userName = ""
    var nick = ""
        get() = if (field.isEmpty()) userName else field

Operátor ==

Tento odstavec bych chtěl věnovat jednomu kolegovi, který již zažil zlo, které se v Javě skrývá za pokusem porovnat typy Integer a int pomocí == 🙂 . V Kotlinu je údajně operátor == jen zkratkou pro volání skutečného .equals(), kde pro porovnání referencí se používá ===. Tzn. tento řádek kódu vrátí true:

println(("a"+"a") == "aa")

A nyní, co udělá tohle?

println(Integer(1000) == 1000)

Dostaneme chybu překladu:

Error:(13, 12) Operator '==' cannot be applied to 'Integer' and 'Int'

Z toho jde pěkně vidět, že code-safety v Kotlinu není omezená pouze na null-checks, ale že sahá podstatně dál, což je dle mého názoru rozhodně plus!

Krásy vs. hype (z jiného soudku)

Jak tak občas procházím Twitter, jsem trochu zděšen, co Kotlinovský hype s některými lidmi dělá a s čím přichází 🙂 . Jako první příklad bych vypíchl Ksoup jakožto pokus o DSL k JSoup v Kotlinu. Nevím jak vám, ale mi to oproti originální JSoup syntaxi rozhodně lepší nepřijde.

Druhým překvapením pro mě byla část oznámení nového Spring Frameworku 5. Jediné, co bych bral jako vylepšení, je možná ta null-safety a restTemplate, ale co se týká odstavce “Spring Web functional API, the Kotlin way” … lidi, to jako vážně? WTF?!

Abych to shrnul, největší krásu Kotlinu vidím v tom, že si z 80% můžete zachovat svůj “java coding style” a ve smyslu lepšího a krásnějšího refaktoringu můžete sáhnout po pár fintičkách, vylepšeních atd., které vám zjednoduší život. Zkrátka já vidím Kotlin jako Javu, jen stručnější, bezpečnější, ale hlavně o dost zábavnější. Avšak důležitou součástí každého programovacího jazyka je jeho kultura, která je něco jako náboženství. Všichni věříme ve stejného Boha, jen se na něho každý díváme trochu jinak. Pokud si Kotlin vyvine vlastni kulturu, která se od té Javovské začne výrazněji vzdalovat, pak je dle mého názoru možné, že si na tom vyláme zuby podobně jako Scala.