Default- und Named Arguments in Kotlin
Default- und Named Arguments machen Kotlin-Aufrufe klarer. Du lernst, wie sie Android-APIs lesbarer und schlanker halten.
Default- und Named Arguments gehören zu den Kotlin-Funktionen, die deinen Android-Code nicht nur kürzer, sondern vor allem verständlicher machen. Du nutzt sie, um sinnvolle Standardwerte direkt in Funktionssignaturen zu beschreiben und Aufrufstellen so zu schreiben, dass andere Entwickler sofort erkennen, welche Entscheidung dort getroffen wird.
Was ist das?
Ein Default Argument ist ein Parameter mit einem Standardwert. Wenn du die Funktion aufrufst, kannst du diesen Parameter weglassen; Kotlin setzt dann den hinterlegten Wert ein. Ein Named Argument ist ein Argument, das du beim Aufruf mit seinem Parameternamen angibst, zum Beispiel isRefreshing = true. Dadurch ist nicht nur der Wert sichtbar, sondern auch seine Bedeutung.
Das löst ein sehr praktisches Problem: Ohne Defaults brauchst du oft mehrere Varianten derselben Funktion. Eine Variante lädt Daten ohne Cache, eine mit Cache, eine mit Timeout, eine mit optionalem Logging. In Java wurde dafür häufig mit Überladungen gearbeitet. In Kotlin kannst du stattdessen eine klare Signatur schreiben und nur die Werte überschreiben, die an der Aufrufstelle wirklich abweichen.
Für Android ist das besonders wichtig, weil du viele kleine APIs baust: ViewModel-Funktionen, Repository-Methoden, Mapper, UI-Composables, Test-Hilfen und Konfigurationsobjekte. Wenn diese APIs zu viele Varianten haben, wird der Code schwerer zu lesen und schwerer zu pflegen. Defaults und Named Arguments helfen dir, die Anzahl der Funktionen niedrig zu halten, ohne an Ausdruckskraft zu verlieren.
Das mentale Modell ist einfach: Die Funktionssignatur beschreibt den Normalfall. Named Arguments beschreiben die Abweichung vom Normalfall. Du musst also nicht bei jedem Aufruf alles erklären, sondern nur das, was für diesen konkreten Aufruf relevant ist.
Wie funktioniert es?
In Kotlin gibst du einen Standardwert direkt am Parameter an. Der Compiler weiß dann, dass dieser Parameter optional ist. Parameter ohne Standardwert müssen weiterhin übergeben werden, außer du verwendest andere Sprachfunktionen wie optionale Typen an anderer Stelle. Defaults ersetzen also keine Modellierung, sondern machen häufige Werte direkt sichtbar.
Named Arguments funktionieren über den Namen des Parameters. Dadurch bist du beim Aufruf nicht mehr strikt an die Reihenfolge gebunden, solange der Aufruf eindeutig bleibt. Das ist nützlich, wenn eine Funktion mehrere Parameter desselben Typs hat. Zwei String-Werte oder mehrere Boolean-Werte sind ohne Namen oft kaum zu verstehen.
Ein typisches Beispiel aus Compose ist ein UI-Element mit vielen optionalen Eigenschaften. Du übergibst den Pflichtinhalt und setzt nur die Optionen, die du brauchst: enabled, modifier, onClick, content. Compose-APIs sind stark darauf ausgelegt, dass Aufrufstellen durch Named Arguments lesbar bleiben. Du siehst direkt, ob ein Button deaktiviert ist, welche Aktion er ausführt und welche Darstellung angepasst wurde.
Auch in der Architektur taucht das Muster ständig auf. In der Data Layer kann ein Repository eine Methode anbieten, die Daten lädt. Der Standardfall ist vielleicht: Nutze Cache, erzwinge kein Netzwerk und verwende ein normales Timeout. Bei einem Pull-to-refresh überschreibst du dann nur forceRefresh = true. So bleibt die Repository-API schlank und die Aufrufstelle sagt klar, warum sie vom Standard abweicht.
Wichtig ist: Defaults sind Teil des API-Designs. Wenn du einen Standardwert änderst, änderst du das Verhalten aller Aufrufstellen, die diesen Parameter nicht explizit setzen. Das kann korrekt sein, ist aber nie nur eine kosmetische Änderung. Bei öffentlichen APIs, gemeinsam genutzten Modulen oder Libraries solltest du solche Änderungen wie echte Verhaltensänderungen behandeln.
Parameternamen sind ebenfalls relevant. In Kotlin-Aufrufen können sie direkt verwendet werden. Wenn du einen Parameternamen später änderst, kann Code brechen, der diesen Namen als Named Argument nutzt. In rein internem Code ist das meist überschaubar. In stabilen APIs solltest du Namen bewusst wählen und nicht leichtfertig ändern.
In der Praxis
Stell dir eine Repository-Methode vor, die Artikel lädt. Du willst den häufigen Fall einfach halten, aber bei Bedarf Cache, Refresh und Limit steuern können. Ohne Defaults würdest du schnell mehrere Methoden oder Überladungen schreiben. Mit Kotlin reicht eine gut benannte Funktion.
class ArticleRepository(
private val remoteDataSource: ArticleRemoteDataSource,
private val localDataSource: ArticleLocalDataSource
) {
suspend fun loadArticles(
category: String,
limit: Int = 20,
forceRefresh: Boolean = false,
includeDrafts: Boolean = false
): List<Article> {
val cached = localDataSource.getArticles(category = category, limit = limit)
if (cached.isNotEmpty() && !forceRefresh) {
return cached
}
val articles = remoteDataSource.fetchArticles(
category = category,
limit = limit,
includeDrafts = includeDrafts
)
localDataSource.saveArticles(articles)
return articles
}
}
Der Normalfall ist kurz:
val articles = repository.loadArticles(category = "kotlin")
Du erkennst: Es geht um Kotlin-Artikel, der Standardwert limit = 20 reicht, Cache ist erlaubt und Entwürfe werden nicht geladen. Für einen Refresh ist die Aufrufstelle weiterhin klar:
val refreshedArticles = repository.loadArticles(
category = "kotlin",
forceRefresh = true
)
Ohne Named Argument wäre repository.loadArticles("kotlin", 20, true, false) deutlich schlechter lesbar. Du müsstest die Signatur kennen oder nachschlagen, um zu verstehen, was true bedeutet. Genau hier liegt der praktische Wert: Named Arguments machen Entscheidungen sichtbar.
Eine gute Entscheidungsregel lautet: Verwende Named Arguments immer dann, wenn ein Wert ohne Namen nicht selbsterklärend ist. Das betrifft besonders Booleans, Zahlen, leere Strings, null und mehrere Parameter desselben Typs. Bei forceRefresh = true versteht man die Absicht. Bei true nicht.
Eine zweite Regel: Setze Defaults nur für echte Normalfälle. Ein Standardwert ist keine Abkürzung, um unbequeme Entscheidungen zu verstecken. Wenn der Aufrufer fachlich entscheiden muss, ob Entwürfe geladen werden, sollte der Parameter vielleicht explizit bleiben. Wenn aber 95 Prozent der Aufrufe keine Entwürfe laden, ist includeDrafts = false ein sinnvoller Default.
Eine typische Stolperfalle ist der zu breite Funktionskopf. Wenn eine Methode sechs oder sieben optionale Parameter hat, wirkt sie zunächst flexibel. In der Praxis wird sie aber schwer zu verstehen und schwer zu testen. Dann ist oft ein eigenes Konfigurationsobjekt, eine kleinere Funktion oder eine klarere Aufteilung besser. Defaults sollen APIs lesbarer machen, nicht jede mögliche Variante in eine Signatur pressen.
Eine weitere Stolperfalle betrifft Tests. Wenn du nur den Standardfall testest, übersiehst du schnell, ob ein abweichender Named-Aufruf korrekt funktioniert. Schreibe deshalb mindestens einen Test für den Default-Pfad und einen für eine bewusste Abweichung, zum Beispiel forceRefresh = true. Im Code-Review kannst du zusätzlich prüfen, ob true, false, 0, 1 oder null ohne Namen auftauchen. Das ist oft ein Hinweis, dass ein Named Argument die Lesbarkeit verbessert.
In Compose gilt dieselbe Idee. Ein kleines UI-Element kann sinnvolle Defaults haben:
@Composable
fun ArticleStatusLabel(
text: String,
modifier: Modifier = Modifier,
isHighlighted: Boolean = false
) {
val color = if (isHighlighted) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
}
Text(
text = text,
modifier = modifier,
color = color
)
}
Der Standardaufruf bleibt ruhig:
ArticleStatusLabel(text = "Gespeichert")
Die besondere Variante ist klar:
ArticleStatusLabel(
text = "Neu",
isHighlighted = true
)
Achte hier auf eine Compose-Konvention: modifier bekommt fast immer einen Default von Modifier und steht üblicherweise weit vorne nach den Pflichtdaten. Dadurch können Aufrufer Layout und Styling von außen steuern, ohne dass du für jede Variante eine neue Composable schreiben musst.
Fazit
Default- und Named Arguments sind kleine Kotlin-Werkzeuge mit großer Wirkung für API-Design, Lesbarkeit und Wartbarkeit. Nutze Defaults für stabile Normalfälle und Named Arguments für Werte, deren Bedeutung an der Aufrufstelle sichtbar sein muss. Prüfe dein Verständnis praktisch: Suche in deinem Projekt nach Aufrufen mit nackten true-, false-, Zahlen- oder null-Werten, ersetze sie gezielt durch Named Arguments und schreibe Tests für mindestens einen Default-Pfad und eine bewusste Abweichung.