Jak se stát Kotlin expertem: Naše nová Kotlin Academy pro začátečníky i pokročilé

Profinit Academy kombinuje výhody tradičního školení s tím nejlepším z e-learningu. Jedná se o kombinaci sledování školicích videí, samostudia z interních materiálů a praktických cvičení a konzultací s vedoucím. Akademií v Profinitu máme již 9, přičemž každá se zaměřuje na určitou skupinou technologií (např. Big Data, AWS atd.), a nedávno byl do tohoto seznamu přidán i Kotlin.

Kotlin akademie je ucelený soubor znalostí, který studenta provede stěžejními oblastmi jazyka Kotlin. Akademie je určena pro všechny úrovně znalostí. Ocení ji především juniorní kolegové, kteří s Kotlinem teprve začínají. Absolvent získá dostatečně kvalitní znalosti, které mu umožní plnohodnotně se zapojit do projektu psaného v Kotlinu.

Akademie je strukturovaná do několika oblastí dle obtížnosti. Jednotlivé kapitoly obsahují přehled témat, která se budou probírat. Témata jsou postavena na oficiální dokumentaci Kotlinu, doplněná o osobní poznatky či doporučení. U složitějších témat je k dispozici více odkazů nahlížejících na problematiku z různých úhlů pohledu. Na konci většiny oblastí je odkaz do Kotlin Koans, kde si účastníci mohou vyzkoušet nově nabyté znalosti na sérii jednoduchých příkladů.

V následujících odstavcích si přiblížíme několik klíčových konceptů jazyka Kotlin a jeho srovnání s jazykem Java, abyste měli lepší představu o tom, jak tento jazyk funguje a jak může posílit vaši vývojářskou dráhu.

Co se skrývá pod pokličkou jazyka Kotlin

Jak mnozí jistě víte, Kotlin se kompiluje do bytecode a běží nad jvm, stejně jako jazyk Java. Vývojáři v Javě mají povědomí o tom, jak jsou dané operace náročné a co se na pozadí děje. U Kotlinu tomu tak být na první pohled nemusí. Pojďme se podívat, jak některé konstrukty Kotlinu vypadají, když se přeloží do Javy.

Intellij IDEA nabízí možnost dekompilace z Kotlinu do Javy pomocí nabídky v menu:

Tools -> Kotlin -> Show Kotlin bytecode -> Decompile

Static function

Kotlin upustil od modifikátoru static, který známe z Javy. Oproti tomu nabízí jiné možnosti, jak pracovat s proměnnými a funkcemi bez vytváření instancí. Jedna z možností je pomocí companion objektu.

class Foo {
    companion object {
        fun debug(message: String) {
            print("Received: $message")
        }
    }
}

 

Při použití v Kotlinu se to chová dle očekávání, k metodě debug lze přistoupit přes volání Foo.debug(). V Javě už to tak hezké není, a pokud bychom opravdu potřebovali volat tuto metodu v Javě, museli bychom k funkci debug přistoupit přes Foo.Companion.debug(), viz výsledek níže po dekompilaci z Kotlinu do Javy.

public class Foo {
    public static final Foo.Companion Companion = new Foo.Companion(null);

    public static final class Companion {
        public final void debug(@NotNull String message) {
            Intrinsics.checkNotNullParameter(message, "message");
            String var2 = "Received: " + message;
            boolean var3 = false;
            System.out.print(var2);
        }

        private Companion() {
        }

        public Companion(DefaultConstructorMarker $constructor_marker) {
            this();
        }
    }
}

 

Řešením optimálního volání z Javy je přidání anotace @JvmStatic. Ta v Javě vygeneruje statickou metodu, která se následně použije jako Foo.debug().

public final class Foo {
  public static final Foo.Companion Companion = new  Foo.Companion(null);

  @JvmStatic
  public static final void debug(@NotNull String message) {
     Companion.debug(message);
  }
} 

 

V izolovaném světě Kotlinu toto řešit nemusíte. Pokud by se ale mohl váš kód psaný v Kotlinu použít při volání v Javě, je vhodné mít podobné anotace na paměti.

When

Funkce when je v Kotlinu velmi používaná, ale ne každý ví, jak se doopravdy chová. Dokážete si tipnout, co by se vypsalo do konzole, pokud bychom provolali následující funkci s argumentem 15?

fun printPrimes(num: Int) {
    when {
        num % 5 == 0 -> print("Divisible by 5")
        num % 3 == 0 -> print("Divisible by 3")
        num % 2 == 0 -> print("Divisible by 2")
    }
}

 

Správná odpověď je pouze „Divisible by 5“. Podle verze Javy, do které to dekompilujete, získáte buď funkci switch, nebo if else strom. V obou případech se hledá pouze první výskyt, ostatní nejsou brány v potaz.

public final void printPrimes(int num) {
    String var2;
    if (num % 5 == 0) {
        var2 = "Divisible by 5";
        System.out.print(var2);
    } else if (num % 3 == 0) {
        var2 = "Divisible by 3";
        System.out.print(var2);
    } else if (num % 2 == 0) {
        var2 = "Divisible by 2";
        System.out.print(var2);
    }
}

 

Streamy vs. Iterable

Od verze Javy 8 jsou programátoři zvyklí používat streamy na veškeré operace nad kolekcemi. V Kotlinu je to ještě jednodušší, protože ve standardní knihovně nalezneme všechny funkce, které poskytují streamy v Javě, přímo nad kolekcemi.

val values = listOf(1, 2, 3)
values.filter { it > 1 }.map { it.toString() }.first()

 

Jednotlivé operace mají za výsledek novou kolekci. Každá další operace se tedy provádí vždy opět nad celou kolekcí. Pokud bychom se podívali, jak to vypadá v Javě, je to poněkud strašidelné.

List values = CollectionsKt.listOf(new Integer[]{1, 2, 3});
Iterable $this$map$iv = values;
Collection destination$iv$iv = new ArrayList();
Iterator var7 = $this$map$iv.iterator();

Object item$iv$iv;
int it;
while(var7.hasNext()) {
    item$iv$iv = var7.next();
    it = ((Number)item$iv$iv).intValue();
    if (it > 1) {
        destination$iv$iv.add(item$iv$iv);
    }
}

$this$map$iv = (List)destination$iv$iv;
destination$iv$iv = new ArrayList(CollectionsKt.collectionSizeOrDefault($this$map$iv, 10));
var7 = $this$map$iv.iterator();

while(var7.hasNext()) {
    item$iv$iv = var7.next();
    it = ((Number)item$iv$iv).intValue();
    String var12 = String.valueOf(it);
    destination$iv$iv.add(var12);
}

CollectionsKt.first((List)destination$iv$iv);

 

Tento přístup se nedoporučuje používat u kolekcí, které jsou objemné a provádí se nad nimi větší množství operací. V takovém případě se doporučuje používat sekvence. Sekvence jsou v Kotlinu ekvivalentem ke streamům v Javě, kdy se operace provádí optimálně vzhledem k ukončující podmínce.
Jediné, co je potřeba udělat navíc, je převést si seznam na sekvenci. Sekvence jsou označovány jako lazy a jednotlivé operace se dekorují další sekvencí, která obsahuje danou operaci. Výsledek se vypočítává až při určení ukončující operace.

val values = listOf(1, 2, 3)
values.asSequence().filter { it > 1 }.map { it.toString() }.first()

List values = CollectionsKt.listOf(new Integer[]{1, 2, 3});
SequencesKt.first(SequencesKt.map(SequencesKt.filter(CollectionsKt.asSequence(values), (Function1)null.INSTANCE), (Function1)null.INSTANCE));

 

Budoucnost ve světě Javy

Kotlin si získal místo ve světě seriózních programovacích jazyků v roce 2017, kdy byl jmenován společností Google oficiálním jazykem pro vývoj aplikací pro platformu Android. V roce 2019 dokonce Kotlin nahradil Javu, jakožto preferovaný programovací jazyk pro platformu Android.

Já jsem se s Kotlinem poprvé setkal v roce 2019 na projektu v přední české bance a na stejném projektu působím doteď. Kotlin mě na první pohled uhranul svojí lehkostí a elegancí, kterou jsem do té doby ve světě Javy neznal. Kotlin nabízí nové možnosti, jak přemýšlet o problémech a jakým způsobem je řešit. Vývojář se mnohem častěji dostane do situace, kdy stejnou věc lze napsat více způsoby a záleží pak čistě na něm, zda si dokáže své řešení obhájit. Toto se dá samozřejmě chápat i negativně. Při nekoordinaci týmu vývojářů se lze snadno dostat do situace, ve které si každý člen dělá, co chce, a výsledkem je nečitelný balast. Každý tým by si měl stanovit vývojové guidelines, které bude dodržovat. V rámci nich má pak každý vývojář svobodu se realizovat dle své libosti.

S budoucností jazyka Kotlin to zatím vypadá slibně. Stále se vydávají nové verze, které opravují nalezené chyby, nebo se přidávají nové funkce. Oči vývojářů Kotlinu by se měly upínat k budoucí verzi Kotlinu 2.0, která představí Kotlin kompilátor K2. Ten zkrátí kompilaci kódu Kotlinu až na polovinu a umožní vývoj nových funkcionalit, které dosud nebyly se starým kompilátorem kompatibilní.

Pokud bychom se podívali na Google trends v porovnání obou jazyků ve světě za posledních 5 let, Kotlin si svojí pozici drží. Je to nicméně graf výskytů vyhledávání na Google, o skutečném počtu aktivních uživatelů to nemusí nic vypovídat.

Google trends

Java se se svými verzemi snaží posouvat vpřed a s každou verzí přijít s něčím novým. V posledních verzích nám Java přinesla novinky, které vývojáři pracující v Kotlinu velmi dobře znají. Java od verze 17 obsahuje vylepšenou verzi funkce switch či sealed classes. Od verze 19 nabízí Java novinku virtuálních vláken, které konkurují coroutines z Kotlinu. Virtuální vlákna jsou nicméně stále v experimentální fázi a jejich použití ještě není ustálené napříč celou komunitou. Uvidíme, co v září přinese nová LTS verze Java 21.

Autor: Lukáš Rubeš