Praca z Kotlinem w aktualnej firmie wrze. Po kilku dniach użytkowania, nie da się ukryć, że jego składania jest nowocześniejsza i przyjemniejsza niż ta znana mi z Javy. Cieszę się, że przed pracą zetknąłem się z Kotlinem po raz pierwszy, aby nie doznać szoku. Dzisiaj chciałbym przedstawić kilka aspektów, na które moim zdaniem warto zwrócić uwagę jeśli chodzi o Kotlina. Natomiast jest jedna rzecz, która mi nie do końca w nim leży, ale o tym powiem na samym końcu. Wpis powstał w oparciu o dokumentację, do której lektury serdecznie zachęcam.

Data class

1
data class User(val nickname: String, val email: String)

W sumie nic okrywczego, bo to samo mamy dostępne w Javie w postaci record. Natomiast składania jest moim zdaniem bardziej kompaktowa. Nie ma zbędnych nawiasów klamrowych. Po prostu w zwykłych nawiasach deklarujemy pola, które są częścią DTO.

Default function values

To jest chyba jedna z lepszych rzeczy. Zamiast pisać coś takiego jak niżej w Javie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int method() {
  return method(3, 2);
}

int method(int b) {
  return method(3, b);
}

int method(int a) {
  return method(a, 2);
}

int method(int a, int b) {
  // do sth
}

Możemy napisać taki sprytny kawałek kodu.

1
2
3
fun method(a: Int = 3, b: Int = 2) {
  // do sth
}

Może powstać pytanie, w jaki sposób skorzystać z domyślnej wartości dla parametry a nadpisując ten reprezentujący parametr b? Wystarczy wywołać metodę, ale trzeba explicite podać nazwę interesującego nas parametru przypisując mu wartość.

1
method(b = 4)

Element ‘in’ collection

Sprawdzenie tego czy dany element jest w kolekcji nie wykonuje się przez metodę contains. Wystarczy skorzystać ze słowa kluczowego in.

1
2
3
if ("apple" in fruits) {
  // do sht
}

Zaprzeczenie jest równe proste.

1
2
3
if ("potato" !in fruits) {
  // do sht
}

Map preparation

Deklaracja mapy z wartościami też jest trywialna i przyjemna dla oka.

1
val namesToPoints = mapOf("Hyzio" to 2, "Dyzio" to 3, "Zyzio" to 10)

Iteration

Sposobów iterowania jest sporo i przypominają mi składnię z Pythona.

1
2
3
4
5
6
7
for (i in 0..100) {
  // do sth
}

for (i in 0..<100) {
  // do sth
}

Pierwszy kawałek kodu iteruje od 0 do 100 włącznie. Natomiast wykorzystanie ‘<’ w pętli for sprawia, że wynikiem operacji będzie iteracja od 0 do 99 z domyślnym skokiem 1. Jeśli chcemy zmienić skok to musimy skorzystać z dodatkowego słowa step np. for (i in 0..100 step 2). Natomiast iteracja w dół jest możliwa dzięki takiej składni for (x in 13 downTo 1). Co jest jeszcze ciekawym skrótowcem to możliwość skorzystania z forEach na zakresie. Wygląda to tak (1..15).forEach { ... }.

Extension functions!

Jeśli miałbym wskazać funkcję, która zrobiła na mnie największe wrażenie (i niepokój) to właśnie extension functions. Pozwala nam ona na dopisanie do dowolnej klasy metody, której nam brakuje w danym momencie.

1
2
3
fun String.doSthFunny() { ... }

"Sample text".doSthFunny()

Jest to w wielu przypadkach bardzo przydane jak brakuje nam jakiejś funkcjonalności w używanej w projekcie bibliotece. Natomiast może też kogoś skusić do lenistwa, aby obejść reguły biznesowe agregowane w jakimś obiekcie.

If not null

Na pewno czego nie można odmówić Kotlinowi to jego walce z NullPointerException. W wielu przypadkach kompilator będzie nas chronił, abyśmy nie popełnili gafy. Natomiast jeśli świadomie wiemy, że pod daną zmienną może kryć się wartość null to możemy skorzystać z takiej składni.

1
2
3
4
5
val someValue: String? = ...

someValue?.let {
    ... // do sth when someValue is not null
}

W ten sposób w elegancki sposób, bez ifów, możemy wykonać kawałek kodu na zmiennej, która może nie mieć przypisanej wartości.

‘With’ on object

Ciekawy koncept znany mi z Groovy. Możemy opakować obiekt w konstrukcję with(...), aby wywoływać na tym obiekcie metody bez używania nazwy zmiennej.

1
2
3
4
5
6
7
8
9
10
11
12
class SomeClass {
    fun doSth()
    fun doSthMore()
    fun calculate(value: Int)
}

val someObject = SomeClass()
with(someObject) {
    doSth()
    doSthMore()
    calculate(10)
}

Przydatne, ale na pewno da się i bez tego żyć.

Łyżka dziegciu

Każdy wie, że nie ma róży bez kolcy. Nie inaczej ma się to z Kotlinem. Przyzwyczajony do modyfikatora package private liczyłem, że bez problemu mogę i z niego skorzystać w tym języku programowania. Niestety nie ma tak prosto… W zamian spotkamy się z jego zamiennikiem internal, który tak naprawdę jest widoczny z poziomu tego samego modułu. I nie, to nie jest pakiet. W dokumentacji zobaczymy, że są to: moduły IntelliJ IDEA, moduły Mavem, source set Gradle czy pliki skompilowane jednym wywołaniem task Ant o nazwie <kotlinc>.

Ten stan rzeczy jest dla mnie problematyczny, bo praktycznie wszystko musi być publiczne. Przez co enkapsulacja modułów musi być zabezpieczana w kompletnie inny sposób. Chyba, że oczywiście się mylę jako laik w nowym środowisku. Jeśli da się to zrobić w sposób znany z Javy albo jest coś innego w arsenale Kotlina to daj znać w komentarzu!