Większość programistów Java stara się śledzić, co dzieje się w społeczności dotyczącej tego języka programowania. Niewątpliwie każdy z tego grona powie, że Java 8 jest dużym krokiem do przodu w tym jakże ciekawym ekosystemie. Na początku wszystkiego powinniśmy jednak zadać sobie podstawowe pytanie, w jakim celu powstała Java?
Każdy, kto miał styczność z innymi językami programowania, w moim przypadku był to PASCAL, DELPHI czy też C/C++ pewnie zgodzi się ze mną, że Java była czymś w rodzaju alternatywy dla C, w pewnej gałęzi zastosowań. Jak można zauważyć składnia Javy zbyt wiele nie odbiega od C++, natomiast to, co wprowadza to mechanizm Garbage Collector’a dbający o zarządzanie pamięcią. W tym wpisie chciałem Wam zwrócić uwagę na kolejną ciekawostkę w obszarze Java i spróbować odpowiedzieć, czym tak naprawdę jest Kotlin?
Nazywając Kotlin frameworkiem do Javy nie miniemy się daleko z prawdą. Powstał w dość zaawansowanym stadium rozwoju Javy. Dodatkowo w procesie jego tworzenia wykonano znacznie mniej zmian koncepcyjnych niż w Java względem C++. Powstał właśnie po to, aby uporządkować kod Javy. Za jego rozwojem stoi czeska firma JetBrains (znana głównie z narzędzi programistycznych tj. IntelliJ), i jak nie łatwo zgadnąć jest przez nich silnie promowany. Z ciekawostek pełne wsparcie dla Kotlina znajdziemy w wersji Community dostępnej na licencji Apache-2.0.
Język ten głównie skupia się na:
- Zwięzłości (Concise)
Pod tym hasłem kryje się minimalizacja ilości stałego kodu. Mowa o nieodłącznych elementach klasy tj. gettery, settery, equals, hashcode, toString, copy. Są to nieodłączne elementy klasy, które nie wnoszą zbyt wiele do kodu a jednak są. Spokojnie mogą być przecież obsługiwane w sposób generyczny. W tym momencie zacytuję tekst usłyszany na jednej z prezentacji „Najlepszy jest ten kod, którego nie ma, gdyż nie trzeba go utrzymywać”. W prawdzie istnieją narzędzia, które możemy zaprząc w Javie. Przykładem takiego narzędzia może być Lombok. Jego użycie polega na dodaniu do projektu zależności, oraz odpowiednich adnotacji nad deklaracją klas/zmiennych mówiących, co ma być wygenerowane. Resztę zrobi kompilator na etapie generowania bytecode’u. Jednak jak to w praktyce bywa nic nie jest idealne, tak też Lombok. Problem pojawia się, gdy chcemy użyć wygenerowany kod w połączeniu z innym dialektem (tj. Kotlin czy Groovy), ponieważ na etapie ich kompilacji nie został on jeszcze wygenerowany.
- Bezpieczeństwie (Safe)
Idąc dalej w koncepcji języka dochodzimy do koszmaru programisty, czyli tzw. NullPointerException. Tu w prawdzie zagorzały Javowiec powie: „Przecież mamy Optional”. Pomijając kwestie nieprawidłowego wykorzystania Optionala doszukałem się kilku wad tego rozwiązania. Optional jest o wiele bardziej przyjazny niż każdorazowe sprawdzanie null, lecz jednak długość linii rośnie w zawrotnym tempie, co niestety pogarsza czytelność kodu .
Poniżej przykład tego samego kodu Java vs Kotlin:
VeryLongClassName variable = matchSth()
String value = Optional.ofNullable(variable).map( VeryLongClassName::getValue).orElse("")
var variable : VeryLongClassName? = matchSth()
var value : String = var?.value ?: ""
- Wrzechstronności (Versatile) i interoperacyjności (Interoperable)
To co najważniejsze (to co może zadecydować o tym czy tego spróbuję), jest fakt, że Kotlin nie odcina się od Javy, lecz tak naprawdę opakowuje kod Javovy w swoją nakładkę. W efekcie możemy tworzyć swój kod w sposób hybrydowy wykorzystując zarówno Javę jak i Kotlin.
- Wygodnej obróbce – Tooling
Nie obyło się bez reklamy produktu JetBrains. Mowa tu o doskonałym wsparciu przez środowisko IntelliJ.
Tyle z marketingowych plusów, przejdźmy do minusów i tego jak to spisuje się w praktyce.
Wady
Mieszane uczucia wywołuje nowa składnia. Patrząc po raz pierwszy na kod w Kotlinie czułem się trochę nieswojo. Składnia jest inna niż to, do czego byłem przyzwyczajony, lecz po stworzeniu kilku klas oraz kilkudziesięciu linii kodu, składnie uważam za intuicyjną.
Poniżej przykład kodu z wykorzystaniem Spring, napisanego w Javie oraz Kotlinie:
@RestController
public class HelloWorldJava {
final HelloAction helloAction;
@Autowired
public HelloWorldJava( HelloAction helloAction ) {
this.helloAction = helloAction;
}
@RequestMapping("/java")
public String helloWorld (String name) {
helloAction.doAction();
return "Hello " + name;
}
}
@RestController
open class HelloWorldKotlin (
open val helloAction: HelloAction) {
@RequestMapping("/kotlin")
fun helloWorld (name : String) : String {
helloAction.doAction()
return "Hello " + name
}
}
Wada, którą odczułem z tego tytułu jest fakt, że kod na etapie kompilacji jest przekładany na kod Javy, co nie współgra np. z biblioteką Lombok (który już mam w swoim kodzie, a jest on także uruchamiany na etapie generowania bytecode’u). Prawdopodobnie można by to wysterować na poziomie skryptu budującego, ale na to nie znalazłem już czasu. Krótko mówiąc mamy kompatybilność z Javą, ale z pluginami niekoniecznie.
Podsumowując – korzystam z tego języka od kilku miesięcy i nie jestem w stanie wymienić więcej jego wad. Składnia, która na początku była co najmniej dziwna przypominającą trochę powrót do korzeni tj. PASCAL w połączeniu z PHP, jest intuicyjna i przyjazna.
Zalety
Do niewątpliwych zalet należy unikanie NullPointerException, które pozwala na określenie, które zmienne mogą być nullem, a które nie, oraz wyłapywanie tych nieprawidłowości z poziomu środowiska programistycznego. Rozwiązanie to w przeciwieństwie do Optionala nie wydłuża geometrycznie kodu.
Kolejnym z plusów jest rozwiązanie (przypominające trochę .NET), które tłumaczy nawigacje po getterach i setterach, jako dostęp „property”. Podobnie jak w .NET tak i w Kotlinie, gettery i setery mogą być nadpisywane na etapie deklaracji zmiennej, a nie odrębnej metody.
Jedną z ciekawszych nowinek, które wprowadza ten język jest dynamiczne typowanie (znane wcześniej z .NET), połączone ze wsparciem środowiska programistycznego.
Checked-exceptions – niby wszyscy wiedzą, że to porażka, ale mimo wszystko są w Javie. Nie da się ukryć, że wyjątki z grupy checked są ogromną kulą u nogi w jakże dynamicznie rozwijającej się Javie. W przypadku deklaracji metod da się w czytelny sposób przerzucić je wyżej. Problem pojawia się, gdy korzystamy z tzw. FunctionalApi, które to wymusza na nas reakcję.
Nie da się ukryć, że możliwość traktowania checked exceptions tak samo jak unchecked znacznie poprawia czytelność kodu. Dociekliwi mogą spytać „a co jeśli chcemy użyć checked-exception, który ma być przekazany do kodu Javy?”, Kotlin pozwoli na to poprzez adnotację @kotlin.jvm.Throws
- Ciekawa funkcjonalność
Modyfikatory Java: jakie są domyślne w Javie chyba pisać nie muszę, jak to wygląda w Kotlinie? Deklarowane klasy, jak też zmienne są domyślnie public (konwertowane na get/set) oraz final. Kwestię widoczności można nadpisać odpowiednim modyfikatorem, natomiast final modyfikatorem open. Wprowadza to pewien element łamigłówki łącząc kotlina z frameworkiem Spring.
Jak każdy język ma swoje wady i zalety.
Bardzo ciekawy artykuł można znaleźć na stronie blogu allegro-tech
https://allegro.tech/2018/05/From-Java-to-Kotlin-and-Back-Again.html