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!