Pierwsze zetknięcie z Kotlinem
Pracując już trochę lat komercyjnie jako programista miałem styczność z językami takimi jak Java, Groovy, TypeScript, JavaScript i Cobol. Nie miałem przyjemności pracować z Kotlinem, o którym słyszałem dużo dobrych rzeczy. Teraz, gdy szykuje się na zmianę zawodową, w nowej pracy będę miał okazję popisać aplikacje właśnie w tym języku. Z tego powodu postanowiłem spojrzeć do dokumentacji i popatrzeć czym Kotlin może mnie zaskoczyć. Z tej okazji tworzę też ten artykuł, aby dać też Tobie pogląd na ten język. Oczywiście o ile nie miałeś/miałaś już z nim styczności.
Definiowanie metod
fun max(a: Int, b: Int): Int {
if (a > b) return a;
return b;
}
Definiując metodę musimy skorzystać ze słowa kluczowego fun, co odróżnia to podejście od Javy. Wskazanie na typ zwracany definiujemy na końcu metody po znaku :, podobnie jak typy parametrów metody. Jednak co najbardziej przykuło moją uwagę to składnia zaprezentowana poniżej.
fun sum(a: Int, b: Int) = a + b
Ciało metody może być prostym wyrażeniem, jednolinijkowcem. W takim przypadku typ zwracany jest dedukowany z wyrażenia. Natomiast jeśli metoda miałaby klasyczny kształt to domyślnym typem zwracanym, gdy żaden nie jest podany, jest void, czyli Unit.
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}
Zmienne - val i var
Kotlin potrafi się domyślić jaki będzie typ zmiennej, który deklarujemy. Nie ma potrzeby jej podawania explicite. Javowcy poznali tą możliwość dopiero w wersji 10. Do definiowania zmiennych w Kotlin służą nam słowa kluczowe var oraz val. Różnica pomiędzy nimi jest taka, że do zmiennej typu var możemy przypisać inną wartość w dalszej części programu. Natomiast raz przypisana wartość do zmiennej typu val zostaje z nią do końca trwania danego bloku kodu. Wydaje mi się, że to działa tak jak słowo kluczowe final w Javie.
val x = 5
var y = 3
// x += 1 // compilation error
y += 1 // y = 4
Niemutowalne klasy
class Point(val x: Int, val y: Int) {}
Tyle wystarczy, aby zdefiniować niemutowalną klasę znaną w Javie jako record od wersji 14. Mamy tutaj strukturę danych reprezentującą punkt. Stworzona instancja klasy Point nie może mieć zmienionych wartości pól x oraz y, ponieważ są one zdefiniowane jako val. Istnieje tylko możliwość odczytania ich wartości. Dodatkowo klasy w Kotlin są domyślnie niemożliwe do rozszerzenia. Żeby zezwolić na taki zabieg musimy to zrobić samodzielnie stosując słowo kluczowe open. Dla mnie tak powinno to działać w każdym języku programowania. Domyślnie większość konstrukcji powinna być na poziomie najbardziej restrykcyjnym, aby to programista świadomie decydował co powinnien widzieć cały świat, a czego nie.
open class Length
class Point(val x: Int, val y: Int): Length {}
String templates
Myślę, że tej funkcjonalności najbardziej brakuje Javowcą, co nie String.format?
val amount = 5
val thing = "apple"
val sentence = "I have ${amount} ${thing}${if (amount > 1) "s" else ""}"
Czy taka składnia nie jest po prostu piękna?
For loop
W Python pamiętam, że w zgrabny sposób można iterować po elementach w kolekcji. To samo zastałem w Kotlinie. Wykorzystanie słowa kluczowego in w pętli for wygląda naprawdę czytelnie. Dodatkowo w prosty sposób możemy również wyciągnąć indeksy kolejnych elementów obecnych w kolekcji, bez korzystania z konstrukcji fori. Java w tym temacie zostaje w tle, no może poza foreach.
val numbers = listOf("one", "two", "three")
for (number in numbers) {
println(number)
}
val numbers = listOf("one", "two", "three")
for (index in numbers.indices) {
println("number at $index is ${numbers[index]}")
}
When statement
Zamiast switch w Kotlin mamy dostępne słowo kluczowe when. I jego składania wygląda naprawdę imponująco. Widać trend w Javie, że właśnie do takiego efektu dążą też twórcy tego języka.
fun resolveDescription(obj: Any): String =
when (obj) {
1 -> "One"
"Hello" -> "Greeting"
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
W when możemy mieszać różne koncepcje. Od prostego przypadku pojedynczej wartości aż po pattern matching, nawet z zaprzeczeniem. Z mojego punktu widzenia naprawdę warto zwrócić uwagę na tą konstrukcję.
Ranges
Tutaj znowu wygląda mi to na koncept podobny do tego z Pythona.
for (x in 1..10) {
print(x)
}
Co ciekawe możemy ze słowa kluczowego in korzystać też w if w kontekście kolekcji. Daje to nam to bardzo przyjemną dla oka składnię.
val x = 6
val y = 10
if (x in 0..y step 2) {
println("fits in range")
}
Jak widać możemy też nadawać skok przy użyciu step jak i dekrementować - downTo.
for (x in 9 downTo 0 step 3) {
print(x) // 9630
}
Lambda expressions
val tools = listOf("c@r", "l@ptop", "mouse", "keybo@rd")
tools
.filter { it.contains("@") }
.map { it.replace("@", "a") }
.sortedBy { it }
.forEach { println(it) }
Wydaje mi się, że koncept it jest zaciągnięty z Groovy. Nie wiem co było pierwsze, ale właśnie stamtąd znam to podejście. W fajny sposób pozwala nam to nie pisać zbędnych zmiennych w lambdzie, które najczęściej powidują konflikt w nazewnictwie. Dodatkowo wykorzystanie nawiasów klamrowych sprawia, że czuję się jakbym pisał testy w Spocku.
Brak null
To chyba największy szok jaki przeżyłem dawno temu, gdy się o tym powiedziałem. Znowu, safety first. Żeby przypisać null do zmiennej muszę to podać z premedytacją wykorzystując znak zapytania. To samo tyczy się wartości zwracanej z metody.
val x = null;
// val y: Int = null;
val z: Int? = null;
fun parseInt(str: String): Int? {
// implementation
}
To mega pomoc w walce z błędem, który kosztował miliard dolarów. Dla mnie to kolejny plus w porównaniu do Javy.
is zamiast instanceof
Za tą zmianą nie tylko idzie semantyka. Również dostajemy w komplecie to za czym Java tak goni.
fun countLetters(obj: Any): Int? {
if (obj is String) {
return obj.length // automatic cast
}
return null
}
Kotlin za nas z automatu robi rzutowanie. Co ciekawe, nie musimy w ogóle robić przypisania do nowej zmiennej jak w Javie. Dalej korzystamy z tej samej zmiennej, ale zawężając jej typ.
Podsumowanie
Po tym krótkim zagłębieniu się w meandry Kotlina muszę przyznać, że podoba mi się ten język. Jego twórcy naprawdę sporo nauczyli się z błędów jakie popełniła starsza koleżanka Kotlina, Java. Także mam nadzieję, że ten krótki wpis również i Ciebie zachęcił do spróbowania swoich sił w Kotlinie. Być może jesteś kolegą lub koleżanką z większym doświadczeniem i mógłbyś/mogłabyś podzielić się w komentarzu minusami jakie dostrzegasz w Kotlinie?