Zum Inhalt springen
Kotlin Anfรคnger 30 min

Funktionen & Lambdas

Funktionen in Kotlin: Default-Parameter, Named Arguments, Extension Functions und das beliebte Lambda mit it und Trailing Closures.

Aktualisiert:
Inhaltsverzeichnis

Funktionen & Lambdas

Kotlin verbindet Objektorientierung mit funktionalen Konzepten - und das fuehlt sich elegant an. Lambdas sind im Alltag allgegenwaertig.

Grundform

fun gruessen(name: String) {
    println("Hallo, $name!")
}

gruessen("Anna")

Rueckgabewert

fun addieren(a: Int, b: Int): Int {
    return a + b
}

Ein-Ausdruck-Funktionen

Wenn der Funktionsrumpf nur ein Ausdruck ist:

fun addieren(a: Int, b: Int): Int = a + b

// Rueckgabetyp kann der Compiler ableiten:
fun addieren2(a: Int, b: Int) = a + b

Kurz und knackig.

Default-Werte

fun gruessen(name: String = "Welt", gruss: String = "Hallo") {
    println("$gruss, $name!")
}

gruessen()                         // "Hallo, Welt!"
gruessen("Anna")                   // "Hallo, Anna!"
gruessen("Leo", "Hi")              // "Hi, Leo!"

Named Arguments

gruessen(gruss = "Moin", name = "Tim")

Besonders hilfreich, wenn du viele Parameter hast - oder nur einen aus der Mitte setzen willst:

gruessen(gruss = "Hi")             // name bleibt bei "Welt"

vararg - Variadic

fun summe(vararg zahlen: Int): Int {
    return zahlen.sum()
}

summe(1, 2, 3)                     // 6
summe(1, 2, 3, 4, 5)               // 15

val liste = intArrayOf(10, 20, 30)
summe(*liste)                      // 60 - Spread-Operator

Funktionen als Werte

Funktionen sind First-Class Citizens:

fun verdoppeln(n: Int): Int = n * 2

fun main() {
    val f: (Int) -> Int = ::verdoppeln
    println(f(5)) // 10
}

::verdoppeln ist eine Function Reference.

Higher-Order Functions

Funktionen, die andere Funktionen nehmen oder zurueckgeben:

fun anwenden(f: (Int) -> Int, wert: Int): Int {
    return f(wert)
}

fun main() {
    println(anwenden(::verdoppeln, 5))               // 10
    println(anwenden({ it * it }, 5))                // 25
}

Lambdas

Anonyme Funktionen - kompakt und sehr haeufig genutzt:

val verdoppeln: (Int) -> Int = { n -> n * 2 }

println(verdoppeln(5))              // 10

Bei einem Parameter: it

val verdoppeln: (Int) -> Int = { it * 2 }

it ist ein impliziter Name fuer das einzige Argument.

Trailing Lambda

Wenn eine Funktion als letzten Parameter eine Lambda nimmt, kann sie ausserhalb der Klammern stehen:

fun anwenden(wert: Int, f: (Int) -> Int): Int = f(wert)

val ergebnis = anwenden(5) { it * 2 }  // kein Komma, Lambda ausserhalb

Das ist ein sehr gelaeufiges Muster in Kotlin - viele APIs sind so aufgebaut.

Collection-Operationen mit Lambdas

Das ist der Ort, wo Lambdas wirklich glaenzen:

val zahlen = listOf(1, 2, 3, 4, 5)

zahlen.map { it * 2 }              // [2, 4, 6, 8, 10]
zahlen.filter { it % 2 == 0 }     // [2, 4]
zahlen.reduce { a, b -> a + b }    // 15
zahlen.sumOf { it }                // 15
zahlen.maxOrNull()                 // 5
zahlen.any { it > 3 }              // true
zahlen.all { it > 0 }              // true
zahlen.find { it > 3 }             // 4

Ketten

val ergebnis = zahlen
    .filter { it > 1 }
    .map { it * 10 }
    .sumOf { it }
println(ergebnis) // 140

Extension Functions

Hier wirdโ€™s magisch - du kannst Funktionen an existierende Typen anhaengen:

fun String.tage(): List<String> = this.split(",").map { it.trim() }

fun main() {
    val wochentage = "Mo, Di, Mi, Do, Fr".tage()
    println(wochentage)  // [Mo, Di, Mi, Do, Fr]
}

this bezieht sich auf den String, auf dem die Funktion aufgerufen wird. Das ist keine Monkey-Patching-Magie, sondern typsicher - der Compiler prueft alles.

Ein haeufiges Beispiel

fun Int.istGerade(): Boolean = this % 2 == 0

val x = 4
println(x.istGerade())   // true
println(7.istGerade())   // false

Warum Extension Functions?

  • API-Design ohne Vererbung
  • Lesbarer Code ("Hallo".tage() statt tage("Hallo"))
  • Viele Kotlin-Standard-Methoden sind so implementiert

Lokale Funktionen

Funktionen innerhalb von Funktionen - nuetzlich, wenn eine Hilfsfunktion nur lokal Sinn macht:

fun verarbeite(text: String): List<String> {
    fun saeubern(s: String): String = s.trim().lowercase()

    return text.split(",")
        .map { saeubern(it) }
        .filter { it.isNotBlank() }
}

Benannte vs. anonyme Funktionen

Manchmal willst du eine anonyme Funktion mit explizitem Rueckgabetyp:

val addiere = fun(a: Int, b: Int): Int {
    return a + b
}

Selten gebraucht - Lambdas reichen fast immer.

let, apply, run, with, also - die Scope Functions

Kotlin hat fuenf kleine Helfer, die oft auftauchen:

let - mit dem Wert arbeiten

val name: String? = "Anna"
name?.let {
    println("Hallo, $it!")
}

apply - Objekt konfigurieren

val p = Person().apply {
    name = "Anna"
    alter = 28
}

Das Objekt selbst wird zurueckgegeben.

run - Block ausfuehren, Ergebnis zurueck

val laenge = "Hallo".run { this.length }

with - wie run, aber mit Argument

val text = with(person) {
    "Name: $name, Alter: $alter"
}

also - Seiteneffekt, Objekt zurueck

val x = berechneWert().also {
    log("Wert: $it")
}

Im Alltag sind let und apply die wichtigsten.

Praktisches Beispiel

data class Produkt(val name: String, val preis: Double, val lager: Int)

fun List<Produkt>.verfuegbar(): List<Produkt> =
    this.filter { it.lager > 0 }

fun List<Produkt>.gesamtwert(): Double =
    this.sumOf { it.preis * it.lager }

fun main() {
    val sortiment = listOf(
        Produkt("Buch", 19.99, 5),
        Produkt("Kaffee", 3.50, 0),
        Produkt("Tasse", 8.00, 10),
    )

    println(sortiment.verfuegbar().map { it.name })  // [Buch, Tasse]
    println(sortiment.gesamtwert())                  // 179.95
}

Extension Functions plus funktionaler Stil - sehr idiomatisches Kotlin.

Zusammenfassung

  • Funktionen mit fun, Ein-Ausdruck-Variante mit =
  • Default-Werte und Named Arguments machen Aufrufe lesbar
  • vararg fuer beliebig viele Argumente
  • Lambdas sind kompakt ({ it * 2 }) - Trailing Lambda ist Standard
  • Extension Functions erweitern Typen ohne Vererbung
  • Scope Functions (let, apply, run, with, also) fuer elegantes Chaining

Im naechsten Kapitel: Klassen, Data Classes und Objects - objektorientiert auf Kotlin-Art.

Zurรผck zum Kotlin Kurs