EMZETT.
Login

Kurz: fun quadrat(x: Int) = x * x // Ausdruckskörper, Typ wird erschlossen

Teil des Kurses Kotlin

Kapitel 4 von 13 im Kurs Kotlin. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Funktionen definieren

fun addiere(a: Int, b: Int): Int {
    return a + b
}
 
fun quadrat(x: Int) = x * x                         // Ausdruckskörper, Typ wird erschlossen
 
fun gruss(name: String = "Welt", anrede: String = "Hallo"): String = "$anrede, $name!"
 
fun protokolliere(text: String) {                   // Unit = kein Rückgabewert (wie void)
    println("[LOG] $text")
}
 
fun summe(vararg zahlen: Int): Int = zahlen.sum()
 
fun minMax(liste: List<Int>): Pair<Int, Int> = Pair(liste.min() ?: 0, liste.max() ?: 0)
 
fun main() {
    println(addiere(2, 3))
    println(quadrat(7))
    println(gruss())
    println(gruss("Mia"))
    println(gruss(anrede = "Hi", name = "Tom"))     // benannte Argumente
    protokolliere("gestartet")
    println(summe(1, 2, 3, 4))
    val zahlen = intArrayOf(5, 6)
    println(summe(*zahlen))                         // Spread-Operator *
    val (klein, gross) = minMax(listOf(4, 9, 1))    // Destrukturierung
    println("$klein $gross")
}

Ausgabe:

5
49
Hallo, Welt!
Hallo, Mia!
Hi, Tom!
[LOG] gestartet
10
11
1 9

Standardwerte und benannte Argumente ersetzen die meisten überladenen Funktionen.

Lokale Funktionen und Rekursion

fun fakultaet(n: Int): Long = if (n <= 1) 1 else n * fakultaet(n - 1)
 
fun fibonacci(n: Int): List<Int> {
    fun naechste(a: Int, b: Int) = b to a + b          // lokale Funktion
    var paar = 0 to 1
    val ergebnis = mutableListOf<Int>()
    repeat(n) {
        ergebnis.add(paar.first)
        paar = naechste(paar.first, paar.second)
    }
    return ergebnis
}
 
tailrec fun ggt(a: Int, b: Int): Int = if (b == 0) a else ggt(b, a % b)     // Endrekursion: keine Stapelüberläufe
 
fun main() {
    println(fakultaet(10))
    println(fibonacci(10))
    println(ggt(48, 18))
}

Ausgabe:

3628800
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
6

Funktionstypen und Lambdas

Funktionen sind Werte. Ein Funktionstyp wird als (Parameter) -> Rückgabe geschrieben:

fun anwenden(x: Int, f: (Int) -> Int): Int = f(x)
 
fun multiplikator(faktor: Int): (Int) -> Int = { x -> x * faktor }
 
fun main() {
    val verdopple: (Int) -> Int = { it * 2 }
    val plus: (Int, Int) -> Int = { a, b -> a + b }
    println(verdopple(21))
    println(plus(3, 4))
    println(anwenden(5) { it + 1 })                // Lambda außerhalb der Klammern
    println(anwenden(5, ::quadrat))                // Funktionsreferenz
    println(multiplikator(3)(7))
 
    var zaehler = 0
    val erhoehe = { zaehler++ }                    // Closure verändert äußere Variable
    erhoehe(); erhoehe()
    println(zaehler)
}
 
fun quadrat(x: Int) = x * x

Ausgabe:

42
7
6
25
21
2

Hat ein Lambda genau einen Parameter, heißt er automatisch it. Ist das Lambda das letzte Argument, steht es hinter den Klammern (trailing lambda).

Erweiterungsfunktionen

Du kannst bestehende Typen um Funktionen erweitern, ohne sie zu verändern:

fun String.istPalindrom(): Boolean {
    val sauber = this.toLowerCase().filter { it.isLetter() }
    return sauber == sauber.reversed()
}
 
fun Int.istGerade() = this % 2 == 0
 
fun <T> List<T>.zweites(): T? = if (size >= 2) this[1] else null
 
val Int.quadrat: Int get() = this * this          // Erweiterungs-Property
 
fun main() {
    println("Anna".istPalindrom())
    println("A man, a plan, a canal: Panama".istPalindrom())
    println(4.istGerade())
    println(listOf(1, 2, 3).zweites())
    println(emptyList<Int>().zweites())
    println(7.quadrat)
}

Ausgabe:

true
true
true
2
null
49

Infix, Operatoren und inline

infix fun Int.hoch(n: Int): Long {
    var r = 1L
    repeat(n) { r *= this }
    return r
}
 
data class Vektor(val x: Int, val y: Int) {
    operator fun plus(o: Vektor) = Vektor(x + o.x, y + o.y)
    operator fun times(k: Int) = Vektor(x * k, y * k)
    operator fun get(i: Int) = if (i == 0) x else y
}
 
inline fun messe(block: () -> Unit): Boolean {       // inline: Lambda wird eingefügt (schneller)
    block()
    return true
}
 
fun main() {
    println(2 hoch 10)
    val v = Vektor(1, 2) + Vektor(3, 4) * 2
    println(v)
    println(v[0])
    println(messe { println("im Block") })
}

Ausgabe:

1024
Vektor(x=7, y=10)
7
im Block
true

Merke

  • fun name(p: Typ = std): Rückgabetyp; Ausdruckskörper = ausdruck
  • Standardwerte, benannte Argumente, vararg und * (Spread)
  • Lambdas { x -> ... } / it; Funktionstypen (Int) -> Int; Referenzen ::name
  • Erweiterungsfunktionen fun String.foo()
  • tailrec, infix, operator, inline für Spezialfälle

Übungsaufgabe

Schreibe eine Erweiterungsfunktion List<Int>.mittelwert() und eine Funktion, die ein Lambda auf jedes Element anwendet.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Strings und Kontrollfluss

Weiter: Null-Sicherheit

Alle Kapitel: Kotlin im Überblick