Android Coden
Android 7 min lesen

Enums in Kotlin für feste Werte

Enums modellieren kleine feste Wertemengen. Du nutzt sie für klare Zustände, Optionen und sichere App-Logik.

Wenn du eine Android-App baust, triffst du ständig auf kleine Gruppen fester Werte: eine Sortierung ist vielleicht Name, Datum oder Bewertung; ein Theme ist System, Hell oder Dunkel; ein Screen lädt Daten, zeigt Inhalt oder meldet einen Fehler. Genau für solche endlichen Wertemengen sind Enums in Kotlin gedacht. Sie machen aus losen Strings oder Zahlen klare Typen, die der Compiler prüfen kann.

Was ist das?

Ein Enum ist ein eigener Typ mit einer festen Anzahl erlaubter Werte. Du definierst also nicht nur einzelne Konstanten, sondern eine geschlossene Menge: Von außen kann niemand heimlich einen vierten Wert hinzufügen, wenn du nur drei Werte vorgesehen hast. Das ist das zentrale Denkmodell: Ein Enum beschreibt eine Auswahl, bei der alle möglichen Fälle im Code bekannt sind.

In Kotlin sieht das zum Beispiel so aus:

enum class SortOption {
    NAME,
    DATE,
    RATING
}

SortOption ist danach ein Typ. Eine Variable dieses Typs kann nur SortOption.NAME, SortOption.DATE oder SortOption.RATING enthalten. Das ist stärker als ein String wie "name" oder "date", weil Tippfehler nicht erst zur Laufzeit auffallen. Wenn du versehentlich SortOption.RATNG schreibst, meldet der Compiler den Fehler sofort.

Im Android-Kontext ist das besonders nützlich, weil viele App-Entscheidungen klar begrenzt sind. Ein Einstellungsdialog bietet eine feste Liste an Theme-Optionen. Eine Liste kann nur nach wenigen Kriterien sortiert werden. Ein UI-Status ist nicht beliebig, sondern bewegt sich zwischen bekannten Zuständen wie Laden, Inhalt und Fehler. Enums geben solchen Entscheidungen einen Namen und machen sie dadurch lesbarer, testbarer und weniger anfällig für versteckte Fehler.

Wichtig ist die Grenze: Enums passen gut für kleine, stabile Mengen. Sie sind nicht dafür gedacht, beliebige Daten aus einem Backend, Nutzernamen, Produkt-IDs oder ständig wachsende Kategorien hart im App-Code abzubilden. Sobald sich Werte häufig ändern oder vom Server kommen, brauchst du meist ein anderes Modell, etwa Datenklassen, Konfigurationsdaten oder eine Datenbanktabelle.

Wie funktioniert es?

Ein Enum wird mit enum class definiert. Die einzelnen Werte stehen am Anfang des Enum-Körpers und werden per Komma getrennt. Jeder Enum-Wert ist ein Objekt dieses Enum-Typs. Dadurch kannst du ihn vergleichen, in when verwenden, in Listen anzeigen oder im Zustand deiner Compose-UI speichern.

Ein einfaches Beispiel für Theme-Optionen:

enum class ThemeChoice {
    SYSTEM,
    LIGHT,
    DARK
}

Der große Vorteil zeigt sich in when. Wenn du alle Enum-Werte behandelst, kann Kotlin prüfen, ob deine Logik vollständig ist:

fun labelFor(choice: ThemeChoice): String {
    return when (choice) {
        ThemeChoice.SYSTEM -> "System"
        ThemeChoice.LIGHT -> "Hell"
        ThemeChoice.DARK -> "Dunkel"
    }
}

Du brauchst hier kein else, weil alle Fälle abgedeckt sind. Das ist mehr als Stil: Wenn du später AMOLED als neuen Wert ergänzt, erinnert dich der Compiler an Stellen, an denen dieser neue Fall noch nicht behandelt wird. Genau dieses Verhalten macht Enums im Alltag wertvoll. Sie helfen dir, Änderungen kontrolliert durch den Code zu ziehen.

Enums können auch Eigenschaften und Funktionen haben. Das ist hilfreich, wenn ein Wert mehr als nur seinen technischen Namen braucht:

enum class SortOption(val label: String) {
    NAME("Name"),
    DATE("Datum"),
    RATING("Bewertung")
}

Dann kannst du in der UI option.label anzeigen, statt an mehreren Stellen dieselben Texte zu wiederholen. Trotzdem solltest du vorsichtig bleiben: Ein Enum ist kein Ersatz für eine vollständige Domain-Schicht. Wenn zu viel Verhalten, Formatierung und Geschäftslogik direkt im Enum landet, wird es schnell unübersichtlich. Für einfache Labels, technische Keys oder kleine Hilfsfunktionen ist es passend; für komplexe Regeln ist oft eine separate Klasse besser.

In Jetpack Compose passen Enums gut zu State. Du kannst eine aktuell gewählte Option als State halten und daraus die Oberfläche ableiten. Compose reagiert dann auf Änderungen und zeichnet die betroffenen Bereiche neu. Das Enum selbst ist dabei nur der Wert; die State-Verwaltung übernimmt Compose über APIs wie remember und mutableStateOf.

Ein weiterer Punkt ist Persistenz. Enum-Namen lassen sich zwar mit name speichern und mit valueOf wieder laden, aber das kann riskant sein. Wenn du einen Enum-Wert umbenennst, passen ältere gespeicherte Daten nicht mehr. Für langfristige Speicherung ist ein stabiler eigener Key oft besser:

enum class ThemeChoice(val storageKey: String) {
    SYSTEM("system"),
    LIGHT("light"),
    DARK("dark")
}

So kannst du den sichtbaren Kotlin-Namen ändern, ohne automatisch gespeicherte Nutzereinstellungen zu beschädigen. In produktiven Apps ist diese Trennung wichtig, weil App-Versionen über längere Zeit mit alten Daten umgehen müssen.

In der Praxis

Stell dir eine kleine Compose-Ansicht vor, in der du eine Aufgabenliste sortieren kannst. Es gibt drei erlaubte Sortierungen. Ohne Enum würdest du vielleicht Strings verwenden: "name", "date", "rating". Das wirkt kurz, führt aber schnell zu Fehlern. Ein einzelner Tippfehler reicht, und eine Bedingung greift nicht mehr. Mit einem Enum ist die Auswahl klar modelliert.

enum class SortOption(val label: String) {
    NAME("Name"),
    DATE("Datum"),
    RATING("Bewertung")
}

data class Task(
    val title: String,
    val createdAt: Long,
    val rating: Int
)

fun sortTasks(tasks: List<Task>, option: SortOption): List<Task> {
    return when (option) {
        SortOption.NAME -> tasks.sortedBy { it.title.lowercase() }
        SortOption.DATE -> tasks.sortedByDescending { it.createdAt }
        SortOption.RATING -> tasks.sortedByDescending { it.rating }
    }
}

In Compose könntest du die Auswahl so verwenden:

@Composable
fun TaskSortControls(
    tasks: List<Task>,
    onSortedTasksChanged: (List<Task>) -> Unit
) {
    var selectedOption by remember { mutableStateOf(SortOption.DATE) }

    Column {
        SortOption.entries.forEach { option ->
            Row {
                RadioButton(
                    selected = selectedOption == option,
                    onClick = {
                        selectedOption = option
                        onSortedTasksChanged(sortTasks(tasks, option))
                    }
                )
                Text(text = option.label)
            }
        }
    }
}

Dieses Beispiel zeigt mehrere typische Vorteile. Erstens ist die UI an eine feste, sichtbare Menge gebunden. Wenn du später eine neue Sortierung ergänzt, erscheint sie automatisch in der Schleife über SortOption.entries. Zweitens bleibt die Sortierlogik typsicher. sortTasks akzeptiert keine beliebigen Strings, sondern nur gültige Optionen. Drittens ist der when-Ausdruck vollständig. Kotlin kann dich also auf fehlende Fälle hinweisen.

Eine praktische Regel lautet: Verwende Enums, wenn du die erlaubten Werte beim Schreiben des Codes vollständig kennst und erwartest, dass sie selten geändert werden. Gute Kandidaten sind Sortieroptionen, Theme-Auswahl, Tab-Typen, Filter-Modi oder einfache UI-Zustände. Schlechte Kandidaten sind Werte, die vom Server frei geliefert werden, fachliche Kataloge mit häufigen Änderungen oder Daten, die Nutzer selbst erzeugen.

Eine häufige Stolperfalle ist die Vermischung von Enum-Werten und UI-Texten. Der technische Name RATING ist nicht automatisch ein guter Text für deine Oberfläche. Nutze dafür eine eigene Eigenschaft, eine Resource-ID oder eine Mapping-Funktion. In echten Android-Apps sollten sichtbare Texte außerdem meist über Ressourcen laufen, damit Übersetzungen und Design-Änderungen sauber möglich bleiben. Für ein Lernbeispiel ist ein label im Enum übersichtlich; in einer mehrsprachigen App würdest du die Anzeige oft anders lösen.

Eine zweite Stolperfalle betrifft gespeicherte Werte. Speichere nicht unüberlegt option.ordinal. Der Ordinalwert ist nur die Position im Enum. Wenn du später die Reihenfolge änderst, zeigen alte Daten plötzlich auf den falschen Wert. Auch name kann problematisch sein, wenn du Werte umbenennst. Stabiler ist ein eigener Key, den du bewusst nicht änderst.

Tests für Enum-Logik sind meist klein, aber nützlich. Du kannst prüfen, ob jede Sortierung das erwartete Ergebnis liefert:

@Test
fun sortTasks_byRating_ordersHighestFirst() {
    val tasks = listOf(
        Task("A", createdAt = 1L, rating = 2),
        Task("B", createdAt = 2L, rating = 5)
    )

    val result = sortTasks(tasks, SortOption.RATING)

    assertEquals(listOf("B", "A"), result.map { it.title })
}

Beim Debuggen lohnt es sich, Enum-Werte direkt im Debugger anzusehen. Du erkennst sofort, welcher Zustand oder welche Option aktiv ist. Im Code-Review solltest du außerdem auf Warnzeichen achten: Wenn du dieselben String-Konstanten an vielen Stellen vergleichst, könnte ein Enum das Modell klarer machen. Wenn ein Enum dagegen immer weiter wächst und fachliche Daten enthält, ist vermutlich ein anderes Datenmodell fällig.

Fazit

Enums sind ein kleines Kotlin-Werkzeug mit großem Praxisnutzen: Du modellierst damit feste Werte, Konstanten und einfache Zustände so, dass der Compiler dich unterstützt. Nutze sie für überschaubare Auswahlmengen wie Sortierungen oder Theme-Optionen, behandle sie vollständig mit when, und sei vorsichtig bei Speicherung, Umbenennung und dynamischen Daten. Prüfe dein Verständnis aktiv, indem du in einer bestehenden App eine String-basierte Auswahl durch ein Enum ersetzt, die betroffenen when-Ausdrücke kompilieren lässt, einen kleinen Test für die Logik schreibst und im Code-Review kontrollierst, ob das Enum wirklich eine stabile, endliche Wertemenge beschreibt.

Quellen (3)
Redaktion

Geschrieben von

Redaktion

Das Redaktionsteam recherchiert und schreibt Artikel zu aktuellen Themen rund um Tech, Lifestyle und Ratgeber.