Przejdź do:
Języki JVM
„Napisz raz, uruchamiaj wszędzie” – taka idea inspirowała twórców Javy. Aby to osiągnąć, potrzebowali czegoś, co uniezależniałoby kod aplikacji od konkretnej platformy sprzętowej. Stworzyli więc wirtualną maszynę Javy (JVM), która interpretuje kod bajtowy niezależnie od urządzenia, na którym jest zainstalowana. Z czasem powstało więcej języków, które kompilowane są do tego samego kodu pośredniego i mogą być uruchamiane poprzez JVM. Wśród nich pojawiły się między innymi Groovy, Scala oraz Kotlin.
Kotlin vs Java
O Kotlinie często mówi się, że jest następcą Javy. Język ten wprowadza pewne ułatwienia, które sprawiają, że kod jest bardziej zwięzły i przejrzysty. Mechanizmy, które Kotlin oferuje, mogą sprawić, że praca programisty będzie efektywniejsza, a kod bardziej odporny na błędy. Ponadto język ten oferuje interoperacyjność z Javą. Oznacza to, że mamy możliwość użycia obu języków w tworzonym przez nas projekcie, jak i korzystania z bibliotek i frameworków znanych z Javy.
Chciałbym zwrócić uwagę na niektóre z różnic pomiędzy Javą a Kotlinem.
Co ma Kotlin, czego nie ma Java? – przykłady
Obsługa typów nie pustych (ang. non-nullable types)
W Kotlinie rozróżniane są typy, które mogą przyjmować wartości puste lub nie. Aby zmienna mogła przyjąć wartość „null”, musimy ją oznaczyć, dodając znak „?”, tak jak w poniższym przykładzie.
1. val a: String? = null
Mechanizm ten pozwala na uniknięcie wielu błędów typu „NullPointerException”, które spowodowane są odwołaniem się do pola nieistniejącego obiektu. Już na poziomie kompilacji programista zostanie poinformowany, że coś jest nie tak.
Istnieje jeszcze operator „!!”, który konwertuje dowolną wartość na typ nieakceptujący wartości pustych. Jeżeli wykorzystamy ten mechanizm, musimy się liczyć z możliwością wystąpienia „NullPointerException”. Ten operator powinien być stosowany bardzo ostrożnie, tylko wtedy, gdy jest się przekonanym, że wartości nie będą puste w momencie ich użycia.
1. val a: String? = null 2. println(a!!.length) // NullPointerException
Gdy nie jesteśmy pewni, czy możemy odwołać się do danego pola, z pomocą przychodzi nam operator bezpiecznego wywołania (ang. safe call operator) „?.”, który sprawdza, czy referencja ma wartość pustą bez wyrzucania wyjątku „NullPointerException”.
1. val a: String? = "Kotlin" 2. val b: String? = null 3. println(a?.length) // 6 4. println(b?.length) // null
Java wraz z nadejściem wersji 8 otrzymała mechanizm pozwalający wyrazić potencjalny brak wartości. Dodana została klasa „Optional”, która wzbogaca zmienną, na której pracujemy, o dodatkowe metody pozwalające obsłużyć brak zasobu. Jednakże Kotlin radzi sobie z tym problemem w sposób generalnie bardziej zwięzły. Użycie operatorów wymaga mniej kodu niż odwoływanie się do wartości opakowanej w dodatkową klasę.
Domniemanie typów
W Kotlinie, inaczej niż w Javie, typ w wielu przypadkach jest określany automatycznie na podstawie kontekstu (możemy pominąć deklarację typu). Ilustruje to poniższy przykład, w którym zmiennej „a” zostanie przydzielony typ „int”.
1. var a = 1 2. a = 5
Podczas deklaracji zmiennych mamy do dyspozycji dwa słowa kluczowe „var” i „val”. Użycie pierwszego z nich („var”) powoduje to, że wartość będzie mogła zostać zmieniona. Natomiast pole zadeklarowane z „val” nie będzie mogło ulec modyfikacji. Zatem kod:
1. val a = 1 2. a = 5 // błąd
zostanie potraktowany jako błąd.
Inteligentne rzutowanie (ang. smart casts)
Kompilator Kotlina jest w stanie samodzielnie zapamiętać sprawdzenie typu i odpowiednio rzutować zmienną na dany typ. W ten sposób w większości przypadków można automatycznie używać zmiennych zgodnie z potrzebami, bez konieczności ręcznego wykonywania oczywistych rzutowań.
Przykład:
1. fun printStringLength(any:Any) { 2. if(any is String) { 3. println(any.length) 4. } 5. }
Zmienna „any” jest automatycznie rzutowana na typ String.
Funkcje rozszerzające (ang. extension functions)
Zdarza się, że jakaś klasa (np. z biblioteki) nie posiada metod, które mogłyby być dla nas bardzo przydatne. Funkcje rozszerzające pozwalają w łatwy sposób rozszerzyć daną klasę o nową funkcjonalność. Takie rozwiązanie pozwala nam po prostu na dodanie nowej funkcji do danej klasy.
Przykład:
1. fun Activity.showToast(message:String){ 2. Toast.makeText(this, message, Toast.LENGTH_LONG).show() 3. }
Wartości domyślne
Kotlin oferuje także wsparcie dla wartości domyślnych. Definiując funkcję, możemy ustawić domyślny parametr, którego nie trzeba już będzie podawać w wywołaniu funkcji.
1. fun sayHello(name: String = „World”){ 2. println(„Hello $name”) 3. } 1. sayHello() // Hello World
„Data classes”
To klasy, które są tylko kontenerem na dane. Domyślnie implementują metody takie jak equals(), hashCode(), toString(), które w Javie należałoby samodzielnie zaimplementować.
1. data class User(val name: String, val age: Int)
Brak getterów i setterów
W Kotlinie gettery i settery są generowane automatycznie. Pozwala to na zaoszczędzenie wielu linii kodu i czasu poświęconego na ich pisanie.
Oczywiście w Javie również powstały narzędzia, które pozwalają zredukować powtarzalny kod. „Lombok” jest biblioteką, która na podstawie adnotacji automatycznie generuje kod w celu ograniczenia tzw. „boilerplate code”.
Czego nie ma Kotlin, a ma to Java?
Typy prymitywne
Podstawowe typy danych takie, jak: boolean, byte, short, int, long, float, double, char. Nie przechowują one obiektów, lecz pojedyncze wartości. A operacje na nich są szybsze niż w przypadku typów złożonych.
„Checked exceptions”
Wyjątki, które muszą być obsłużone przez programistę. Żeby skompilować kod, programista musi „naprawić” błąd wyrzucony przez wywołaną funkcję (użyć instrukcji try…catch…).
Jest to kontrowersyjny koncept. Niektórzy uważają, że nie poprawia to jakości kodu, a zmniejsza produktywność programisty. Narzucenie konieczności obsługi wyjątków czasami skutkuje pustymi blokami „catch”. Obsługa wyjątków np. typu „IOException” w niskopoziomowej metodzie nie zawsze ma sens. Być może lepiej byłoby obsłużyć je gdzieś na wyższym poziomie. Kotlin świadomie nie przyjął tego konceptu.
Słowo kluczowe „static”
Kotlin nie posiada słowa kluczowego „static”. Zamiast tego ma mechanizmy takie jak „companion object”, czy deklarowanie zmiennej/funkcji poza klasą (ang. top-level declaration).
“Companion object” to obiekt typu “singleton”, powiązany z klasą, w której się znajduje, i zawierający pola i metody, do których możemy się odwołać w ten sam sposób co do elementów statycznych w Javie.
1. class MyClass { 2. companion object Factory { 3. fun create(): MyClass = MyClass() 4. } 5. } 1. val myClass = MyClass.create()
Zastosowanie języka Kotlin
Odkąd w 2019 r. Google ogłosiło Kotlin rekomendowanym językiem dla aplikacji na ich system operacyjny na urządzenia mobilne, może być on przez wielu developerów kojarzony głównie z Androidem. Jednak warto pamiętać, że to niejedyne zastosowanie Kotlina.
Język ten sprawdza się też w przypadku tworzenia aplikacji serwerowych czy desktopowych. Ogólnie rzecz ujmując, sprawdzi się wszędzie tam, gdzie może być używana Java (z ciekawostek – Kotlin został wykorzystany przy tworzeniu zintegrowanego środowiska developerskiego IntelliJ IDEA)
Projekt Kotlin Multiplatform
Warto wspomnieć też o tym, że rozwijany jest projekt o nazwie „Kotlin Multiplatform”. Dzięki niemu możliwe jest pisanie wspólnego kodu logiki biznesowej aplikacji, która będzie działać na wielu platformach, takich jak między innymi Android, iOS, Windows, Linux, macOS oraz jako aplikacja webowa.
Kotlin Android Development
Kotlin zyskał ogromną popularność dzięki Androidowi. Obecnie większość nowych aplikacji na tę platformę powstaje właśnie w tym języku. Jedne z najbardziej znanych aplikacji na Androida stworzonych z użyciem Kotlina to: Slack, Trello, Evernote, Pinterest, Tinder czy Uber.
Także nowe narzędzia dla Android developerów, takie jak np. Jetpack Compose, wykorzystują cechy Kotlina i wymagają jego użycia. W wielu nowo powstających tutorialach przykładowy kod często jest również zaprezentowany w tym właśnie języku.
Programuję w Javie – czy warto się przestawić na Kotlin?
Niełatwo jest porównywać języki programowania, ponieważ każdy ma swoje wady i zalety. Zarówno Kotlin, jak i Java ciągle są rozwijane i oferują coraz to nowsze mechanizmy. Java jest z nami od 1991 roku i od wielu lat znajduje się w czołówce najpopularniejszych języków programowania na świecie. Przez ten czas powstało wiele projektów. Czy warto więc się przestawić? To zależy.
Czy Kotlin zastąpi Javę?
Choć dla tysięcy programistów na świecie królowa jest tylko jedna, wiele argumentów wskazuje na to, że Kotlin to godny następca Javy. Najważniejsze z nich to:
- Czytelniejszy kod
- Odporność na błędy typu „NullPointerException”
- Mechanizmy wspierające programowanie funkcyjne czy narzędzia (takie jak np. Jetpack Compose dla Androida)
Podsumowanie
Moim zdaniem, rozpoczynając nowy projekt, warto się przestawić na język stworzony przez firmę JetBrains. Pozostaje jednak jeszcze pytanie, co z istniejącymi projektami napisanymi w Javie? Czy warto je migrować do Kotlina? Kotlin został zaprojektowany tak, żeby zachować pełną kompatybilność z Javą. Migracja może więc następować w sposób stopniowy. W bazie kodu mogą być dwa języki. Wiele firm wykorzystuje ten fakt i przekształca swoje aplikacje, aby mogły być one dalej rozwijane z wykorzystaniem zalet Kotlina. Jednakże dwa języki w kodzie mogą powodować pewne zamieszanie. Warto więc, jak zawsze, zachować szerszy kontekst i dopasować język indywidualnie do potrzeb i specyfiki projektu.
Kotlin – skąd czerpać wiedzę?
Strony internetowe
- Oficjalna strona Kotlina – https://kotlinlang.org/
- https://www.w3schools.com/kotlin/index.php
- Kotlin Academy – https://blog.kotlin-academy.com/
Książki:
- „Kotlin w Akcji” – Dmitry Jemerov, Svetlana Isakova
Materiały dla programistów Android:
Kanały na YouTube:
Przejdź do: