Kotlin, alternatief voor Java – deel 1
fun calc(a: Int = 3, b: Int = 2,
c: Int = 1)= a * b +c
Deze functie zou dan bijvoorbeeld als volgt gebruikt kunnen worden:
calc() // 7 calc(b = 4) // 13 calc(c = 6, a = 5) // 16
Zoals in de laatste regel te zien is, hoef je je bij de genoemde parameters met standaardwaarde ook niet aan de precieze volgorde van de parameters te houden.
Met Kotlin zijn ook functies van een hogere orde mogelijk. Een functie kan dus bijvoorbeeld een andere functie als parameter meekrijgen en uitvoeren:
fun times(n: Int, f:(Int) -> Unit){ for (i in 1..n){
f(i)
}
}
De functie times krijgt bij een aanroep een lambda-expressie mee die n keer uitgevoerd wordt en zelf de actuele teller meekrijgt. Het volgende voorbeeld zet op die manier de getallen 1, 2, 3, 4 en 5 op het scherm:
times(5,{ x -> println(x)}
Als er maar één parameter is, kun je de parameterlijst in de lambda-expressie (× ->) ook weglaten en in plaats daarvan it gebruiken.
times(5,{ println(it) })
Maar aangezien in dit geval puur een andere functie – println – aangeroepen wordt, kun je ook simpelweg een functiereferentie gebruiken:
times(5, ::println)
Om tot een syntax te komen zoals bij ingebouwde controlestructuren als if, mag je de lambda-expressie voor de laatste functieparameter ook achter de parameterlijst zetten:
times(5){ x ->
println(x)
}
Zo zou je bijvoorbeeld heel elegant een functie kunnen gebruiken om transacties beschikbaar te maken:
transactional {
// instructies in transactie
}
Je kunt functies ook binnen andere functies definiëren om de scope van de binnenste functie zo gericht mogelijk te houden:
fun a(p: Iterable<Int>): Int { fun b(x: Int)= x * x–1 return p.map { b(x) }.sum()
}
Uitbreidingsfuncties
Je hebt vast wel eens gewenst dat je een klasse uit een vreemde bibliotheek achteraf met een functie kon uitbreiden zonder daarvoor eerst een nieuwe klasse te moeten afleiden. Als dat überhaupt mogelijk is, want overerving is vaak niet toegestaan – en daar zijn goede redenen voor. In Kotlin heb je hiervoor uitbreidingsfuncties (extensions). Zo kun je bijvoorbeeld voor de klasse Int een functie definiëren die een actie x keer uitvoert:
fun Int.times(action:(Int) -> Unit) {
for (i in 1..this){
action(i)
}
}
5.times {print(it)}
Het voorbeeld voert '12345' uit.
Om van een functie een uitbreidingsfunctie te maken, hoef je alleen het uit te breiden type (hier Int) met een punt voor de functienaam te zetten. Het object van het uitgebreide type is binnen de functie beschikbaar als this. In het voorbeeld is this het getal 5 waarmee de functie aangeroepen wordt.
Slimme typeconversie
Als je met if of when een type controleert, converteert Kotlin het type automatisch. In het volgende voorbeeld heb je direct toegang tot de eigenschap length van de klasse String omdat de compiler 'weet' dat het na een succesvolle controle om een string moet gaan. Daar heb je geen expliciete typeconversie voor nodig:
fun measure(something: Any){ if (m is String){
println(m.length)
}
}
Hetzelfde principe komt trouwens ook naar voren als je op null controleert. Dan wordt een 'nullable' type namelijk omgezet naar het bijbehorende basistype, dus bijvoorbeeld String? naar String.
Nog veel meer
Klassen en overerving, lokale variabelen, operatoren – Kotlin biedt op nog veel meer gebieden wezenlijke vereenvoudigingen in vergelijking met Java. Meer daarover willen we uitleggen in een tweede deel van deze inleiding.
Als je niet zo lang wilt wachten en alvast eens met Kotlin wilt experimenteren, kun je je eerste stappen zetten op try.kotlinlang.org. Deze online runtimeomgeving biedt zelfs syntaxaanvulling en kan Java-code omzetten naar Kotlin. Als je professioneel aan de slag wilt met Kotlin, is de laatste versie van IntelliJ IDEA de eerste keus. Ook de gratis Community Edition ondersteunt Kotlin. Daarvoor heb je wel JDK (versie 6 of hoger) nodig.
(hhe)