Guard Conditions in when Statements in Kotlin 2.1.0
Kotlin
Guard Conditions in when Statements in Kotlin 2.1.0
Akshay Nandwana
January 7, 2025
5 min read
36 views

Kotlin 2.1 introduces guard conditions in when statements, a feature that allows combining condition matching and boolean checks in a concise and readable way.

Before Kotlin 2.1 introduced guard conditions in when statements, developers had to rely on nested if statements or multiple branches to achieve the same functionality.

For Android developers, this feature simplifies handling complex scenarios like API responses, user inputs, and dynamic UI behavior.

In this blog, we’ll explore:

  1. How to enable this feature.

  2. Using guard conditions with when.

  3. Practical examples, including else if branches.

  4. Tips for clean, maintainable code.

What Are Guard Conditions in when Statements?

A guard condition is an additional if clause in a when branch that allows you to add boolean checks for a matched condition. This reduces nested if statements and makes your code more expressive.

Syntax

kotlin
when (val subject = expression) {
    condition if guardExpression -> { /* action */ }
    else if anotherCondition -> { /* alternate action */ }
    else -> { /* fallback action */ }
}

Let’s compare how code looks in older Kotlin versions versus the new Kotlin 2.1 feature, and why this enhancement is so useful.

How when Worked in Older Kotlin Versions

In previous versions of Kotlin (before 2.1), handling additional boolean checks with when required writing nested if statements or duplicating logic for different cases. Here’s an example:

Old Kotlin Implementation

kotlin
fun validateCredentialsOld(username: String, password: String): String {
    return when (username) {
        "admin" -> {
            if (password.isEmpty()) {
                "Password cannot be empty for admin."
            } else if (password == "admin123") {
                "Welcome, Admin!"
            } else {
                "Invalid admin password."
            }
        }
        else -> {
            if (username.isEmpty()) {
                "Username cannot be empty."
            } else if (password.isEmpty()) {
                "Password cannot be empty."
            } else {
                "Invalid credentials. Please try again."
            }
        }
    }
}

In the old approach:

  1. We needed nested if statements inside when branches to handle additional conditions.

  2. Logic became harder to follow and less readable, especially as complexity increased.

  3. Repeated checks (e.g., password.isEmpty()) led to code duplication.

How the New Feature Simplifies This

With Kotlin 2.1’s guard conditions, additional checks can now be written inline within when branches using the if keyword. This avoids nesting, reduces duplication, and improves readability.

New Kotlin Implementation (with Guard Conditions)

kotlin
fun validateCredentials(username: String, password: String): String {
    return when (username) {
        "admin" if password.isEmpty() -> "Password cannot be empty for admin."
        "admin" if password == "admin123" -> "Welcome, Admin!"
        else if username.isEmpty() -> "Username cannot be empty."
        else if password.isEmpty() -> "Password cannot be empty."
        else -> "Invalid credentials. Please try again."
    }
}

Advantages of the New Feature:

  1. No nesting: Guard conditions make additional checks concise and inline.

  2. Improved readability: Each branch is self-contained, making the logic easier to understand.

  3. Reduced duplication: Avoid repeating logic in multiple branches.

How to Implement Guard Conditions in Old Kotlin Versions

If you're working in a version of Kotlin that doesn’t support guard conditions, you can replicate similar behavior by:

  1. Using if blocks inside when branches.

  2. Refactoring into helper functions to reduce nested logic.

Example: Replicating Guard Conditions in Older Versions

Here’s how you can simulate guard conditions in Kotlin versions before 2.1:

kotlin
fun validateCredentialsOld(username: String, password: String): String {
    return when {
        username == "admin" && password.isEmpty() -> "Password cannot be empty for admin."
        username == "admin" && password == "admin123" -> "Welcome, Admin!"
        username.isEmpty() -> "Username cannot be empty."
        password.isEmpty() -> "Password cannot be empty."
        else -> "Invalid credentials. Please try again."
    }
}

In this example:

  • when uses compound conditions (&&) to handle cases.

  • The nested if statements are removed by moving the logic directly into the condition checks.

Use Cases in Android Development

1. Validating User Input with else if

Consider a login form where you validate the username and password with nuanced error handling:

kotlin
fun validateCredentials(username: String, password: String): String {
    return when (username) {
        "admin" if password.isEmpty() -> "Password cannot be empty for admin."
        "admin" if password == "admin123" -> "Welcome, Admin!"
        else if username.isEmpty() -> "Username cannot be empty."
        else if password.isEmpty() -> "Password cannot be empty."
        else -> "Invalid credentials. Please try again."
    }
}

Key Points:

  • The else if branches allow catching additional cases like empty usernames or passwords.

  • The else branch acts as a fallback for unmatched cases.

2. Processing API Responses with else if

When handling API responses, you often need to process both success and error cases. Guard conditions with else if simplify this.

kotlin
fun handleApiResponse(statusCode: Int, errorMessage: String?): String {
    return when (statusCode) {
        200 if errorMessage == null -> "Success! Data loaded."
        400 if errorMessage != null -> "Client Error: $errorMessage"
        500 -> "Server Error: Try again later."
        else if statusCode in 401..499 -> "Authentication Error: Code $statusCode"
        else -> "Unknown Response."
    }
}

Key Points:

  • The else if handles a range of status codes (401–499) as authentication errors.

  • Additional error handling logic stays clean and focused.

3. Customizing Themes Based on User Preferences

Dynamic themes are common in Android apps. Use guard conditions to handle multiple parameters like orientation and user preferences.

kotlin
fun getThemeColor(orientation: Int, isDarkMode: Boolean, isHighContrast: Boolean): Int {
    return when (orientation) {
        Configuration.ORIENTATION_PORTRAIT if isDarkMode -> Color.BLACK
        Configuration.ORIENTATION_PORTRAIT if isHighContrast -> Color.YELLOW
        Configuration.ORIENTATION_LANDSCAPE if isDarkMode -> Color.DARK_GRAY
        else if isHighContrast -> Color.ORANGE
        else -> Color.WHITE
    }
}

Key Points:

  • Guard conditions (if isDarkMode, if isHighContrast) make conditions specific and easier to read.

  • The else if branch ensures fallback customization for high contrast mode.

Using Guard Conditions in Jetpack Compose

Jetpack Compose simplifies UI updates, and guard conditions make declarative logic easier. Here’s an example with else if for dynamic styling:

kotlin
@Composable
fun UserRoleDisplay(role: String, isVerified: Boolean, isPremium: Boolean) {
    val textStyle = when (role) {
        "Admin" if isVerified -> TextStyle(color = Color.Red, fontWeight = FontWeight.Bold)
        "User" if isVerified -> TextStyle(color = Color.Green, fontWeight = FontWeight.Normal)
        "Guest" -> TextStyle(color = Color.Gray, fontStyle = FontStyle.Italic)
        else if isPremium -> TextStyle(color = Color.Gold, fontWeight = FontWeight.Medium)
        else -> TextStyle(color = Color.Black)
    }

    Text(
        text = "Role: $role",
        style = textStyle
    )
}

Key Points:

  • Guard conditions (if isVerified, if isPremium) determine the user’s style dynamically.

  • The else if branch ensures premium users have a unique style even if they’re not explicitly matched by the when conditions.

Best Practices for Guard Conditions

  1. Enable Fallbacks: Always include an else branch to handle unexpected cases.

  2. Use else if Wisely: Avoid chaining too many else if branches, as it may reduce readability. Use helper functions for complex conditions.

  3. Keep It Readable: Use guard conditions only when they improve clarity. Overcomplicating a single branch can lead to hard-to-read code.

Enabling Guard Conditions in Kotlin

The guard conditions feature is currently experimental in Kotlin 2.1. To use it, you need to enable it in your project.

1. Using the Command Line

If you're running Kotlin files from the command line, use the following command:

kotlin
kotlinc -Xwhen-guards main.kt

2. Configuring in Gradle

For Gradle-based projects (typical in Android development), enable it in your build.gradle.kts:

kotlin
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xwhen-guards")
    }
}

Conclusion

Guard conditions in when statements are a fantastic addition in Kotlin 2.1, enabling more expressive and readable code. For Android developers, this feature is particularly valuable in scenarios like:

  • Validating user input.

  • Handling API responses.

  • Customizing UI themes and styles.

By enabling this feature and using it wisely, you can streamline logic handling in your Kotlin projects. Experiment with when guards and elevate your Android development experience!

Akshay Nandwana
Founder AndroidEngineers

You can connect with me on:


Book 1:1 Session here
Click Here

Join our upcoming classes
https://www.androidengineers.in/courses

Share This Article
Stay Updated

Get the latest Android development articles delivered to your inbox.