Aus "Kotlin OOP" kennst du Funktionen als Methoden von Klassen. Funktionale Programmierung (FP)
geht einen Schritt weiter: Funktionen sind Werte, genau wie ein Int oder ein String. Man kann
sie einer Variablen zuweisen, als Parameter übergeben oder aus einer Funktion zurückgeben. Kotlin
nennt das "Funktionen als First-Class Citizens".
Damit eine Variable eine Funktion als Wert halten kann, braucht sie einen Funktionstyp —
die Typ-Signatur einer Funktion, geschrieben als (Parametertypen) -> Rückgabetyp:
val versandkostenBerechnung: (Double) -> Double = { gewicht -> gewicht * 0.5 }
val nichtsTun: () -> Unit = { println("Kein Parameter, keine Rückgabe") }
versandkostenBerechnung ist vom Typ (Double) -> Double: eine Funktion, die einen Double
entgegennimmt und einen Double zurückgibt. Der Wert dahinter ist eine Lambda-Expression —
eine Funktion ohne eigenen Namen, geschrieben in geschweiften Klammern:
{ gewicht -> gewicht * 0.5 }
Vor dem -> stehen die Parameter, danach der Rumpf. Der letzte Ausdruck im Rumpf ist automatisch
der Rückgabewert — kein return nötig. Hat die Lambda genau einen Parameter, kann man den Namen
weglassen und stattdessen it benutzen:
val verdoppeln: (Int) -> Int = { it * 2 }
Wenn du den Rückgabetyp explizit brauchst (z. B. bei einem leeren Funktionskörper), gibt es als Alternative zur Lambda die anonyme Funktion:
val summe = fun(a: Int, b: Int): Int = a + b
Beide Schreibweisen erzeugen einen Wert vom Typ (Int, Int) -> Int — für die meisten Fälle ist die
kürzere Lambda-Syntax die übliche Wahl.
Ein praktischer Fall aus der Anwendungsentwicklung: unterschiedliche Rabattstrategien als austauschbare Werte:
var rabattStrategie: (Double) -> Double = { preis -> preis * 0.95 } // 5 % Rabatt
println(rabattStrategie(200.0)) // 190.0
rabattStrategie = { preis -> preis } // Rabattaktion vorbei, keine Reduktion mehr
println(rabattStrategie(200.0)) // 200.0
Ohne Funktionstypen müsste man dafür ein enum oder mehrere if-Zweige schreiben. Mit
Funktionen als Werten tauscht man einfach die Funktion selbst aus.
Lies den Code genau durch, bevor du ihn ausführst.
fun main() {
var rabatt: (Double) -> Double = { preis -> preis * 0.9 }
val keinRabatt: (Double) -> Double = { preis -> preis }
println(rabatt(100.0))
rabatt = keinRabatt
println(rabatt(100.0))
}
rabatt ist ein var, das eine Funktion als Wert hält. Was gibt der zweite println-Aufruf aus, nachdem rabatt neu zugewiesen wurde?
Ein Versandunternehmen berechnet Versandkosten abhängig vom Gewicht eines Pakets. Deklariere eine val namens standardVersand vom Funktionstyp (Double) -> Double, die für ein gegebenes Gewicht in Kilogramm die Versandkosten als gewicht * 1.5 berechnet. Deklariere danach eine var namens versandKosten, die zunächst auf standardVersand zeigt. Gib versandKosten(4.0) aus. Weise versandKosten anschließend eine neue Lambda zu, die für Expressversand gewicht * 1.5 + 10.0 berechnet, und gib versandKosten(4.0) erneut aus.
Bevor du einen Hint bekommst: Was hast du schon probiert, und woran hängt es genau?
1. Welcher Funktionstyp beschreibt eine Funktion, die zwei Int-Werte nimmt und einen Boolean zurückgibt?
2. Was passiert, wenn eine Lambda mit genau einem Parameter keinen Parameternamen angibt?
3. Warum musste versandKosten in der Aufgabe ein var statt ein val sein?