The kotlin when statement is a conditional expression that evaluates multiple branches sequentially until it finds a matching condition. Unlike Java’s switch statement, the when expression kotlin can work with any data type, supports complex conditions, and doesn’t require break statements.
The kotlin when expression offers several advantages over traditional switch statements:
is
checksin
operatorThe fundamental kotlin when syntax follows this pattern:
when (variable) {
condition1 -> action1
condition2 -> action2
else -> defaultAction
}
Here’s how to use when in kotlin for basic value matching:
fun getDayType(day: String): String {
return when (day) {
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "Weekday"
"Saturday", "Sunday" -> "Weekend"
else -> "Invalid day"
}
}
This example demonstrates multiple values matching the same condition using comma separation.
The kotlin when expression can function in two ways:
As Expression (returns value):
val result = when (score) {
in 90..100 -> "Excellent"
in 80..89 -> "Good"
in 70..79 -> "Average"
else -> "Needs Improvement"
}
As Statement (performs action):
when (userRole) {
"Admin" -> grantAdminAccess()
"User" -> grantUserAccess()
"Guest" -> grantGuestAccess()
}
The when expression kotlin can be used without a subject, acting as a replacement for if-else chains:
fun validatePassword(password: String) {
when {
password.length < 8 -> throw IllegalArgumentException("Password too short")
!password.any { it.isDigit() } -> throw IllegalArgumentException("Password must contain numbers")
!password.any { it.isUpperCase() } -> throw IllegalArgumentException("Password must contain uppercase")
password.contains(" ") -> throw IllegalArgumentException("Password cannot contain spaces")
}
}
This pattern is particularly useful for complex conditional logic where you need to check multiple boolean expressions.
The kotlin when expression supports range checking using the in
operator:
fun categorizeAge(age: Int): String = when (age) {
in 0..12 -> "Child"
in 13..19 -> "Teenager"
in 20..64 -> "Adult"
in 65..120 -> "Senior"
else -> "Invalid age"
}
You can check if values exist in collections:
fun checkValidGrade(grade: Char): Boolean {
val validGrades = listOf('A', 'B', 'C', 'D', 'F')
return when (grade) {
in validGrades -> true
else -> false
}
}
The when expression in kotlin provides automatic smart casting when using is
checks:
fun processData(data: Any): String = when (data) {
is String -> "Text: ${data.uppercase()}" // data is automatically cast to String
is Int -> "Number: ${data * 2}" // data is automatically cast to Int
is List<*> -> "List with ${data.size} items" // data is automatically cast to List
is Boolean -> if (data) "True value" else "False value"
else -> "Unknown type: ${data::class.simpleName}"
}
One of the most powerful features of kotlin when expression is its integration with sealed classes for exhaustive pattern matching:
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val exception: Throwable) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
}
fun handleNetworkResult(result: NetworkResult<String>) = when (result) {
is NetworkResult.Success -> showData(result.data)
is NetworkResult.Error -> showError(result.exception.message ?: "Unknown error")
NetworkResult.Loading -> showLoadingIndicator()
// No else clause needed - compiler ensures exhaustiveness
}
Kotlin supports guard conditions in when expressions, allowing additional conditions after the primary match:
sealed class Animal {
data class Dog(val breed: String, val isTrained: Boolean) : Animal()
data class Cat(val isIndoor: Boolean, val age: Int) : Animal()
}
fun handleAnimal(animal: Animal) = when (animal) {
is Animal.Dog if animal.isTrained -> "Well-behaved ${animal.breed}"
is Animal.Dog -> "Untrained ${animal.breed} needs training"
is Animal.Cat if animal.isIndoor && animal.age < 2 -> "Young indoor kitten"
is Animal.Cat if animal.isIndoor -> "Indoor cat"
is Animal.Cat -> "Outdoor cat"
}
You can nest when expressions for complex decision trees:
fun calculateDiscount(customerType: String, orderAmount: Double): Double {
return when (customerType) {
"Premium" -> when {
orderAmount >= 1000 -> 0.20
orderAmount >= 500 -> 0.15
else -> 0.10
}
"Regular" -> when {
orderAmount >= 1000 -> 0.10
orderAmount >= 500 -> 0.05
else -> 0.0
}
"New" -> 0.05
else -> 0.0
}
}
Branch conditions can include function calls and complex expressions:
class UserValidator {
fun isValidEmail(email: String): Boolean = email.contains("@") && email.contains(".")
fun isValidAge(age: Int): Boolean = age in 13..120
fun validateUser(email: String, age: Int, role: String) = when {
!isValidEmail(email) -> "Invalid email format"
!isValidAge(age) -> "Invalid age"
role.isBlank() -> "Role cannot be empty"
role.length < 3 -> "Role too short"
else -> "Valid user"
}
}
The kotlin when expression works seamlessly with custom objects:
data class UserPermission(val level: Int, val department: String)
fun checkAccess(permission: UserPermission, resource: String): Boolean = when {
permission.level >= 10 -> true // Admin access
permission.level >= 5 && permission.department == "IT" && resource.startsWith("tech") -> true
permission.level >= 3 && resource.startsWith("public") -> true
permission.level >= 1 && resource == "basic-info" -> true
else -> false
}
You can combine when expressions with lambda functions for functional programming patterns:
fun processUserActions(actions: List<String>) {
actions.forEach { action ->
when (action.lowercase()) {
"login" -> authenticateUser()
"logout" -> terminateSession()
"refresh" -> refreshUserData()
else -> logUnknownAction(action)
}
}
}
// Using when with filter and map
fun categorizeNumbers(numbers: List<Int>): Map<String, List<Int>> {
return numbers.groupBy { number ->
when {
number < 0 -> "negative"
number == 0 -> "zero"
number % 2 == 0 -> "positive-even"
else -> "positive-odd"
}
}
}
The kotlin when expression is optimized by the compiler based on the type of conditions:
// Efficient constant matching - compiled to jump table
fun getStatusCode(status: String) = when (status) {
"SUCCESS" -> 200
"NOT_FOUND" -> 404
"ERROR" -> 500
else -> 0
}
// Efficient range checking
fun categorizeTemperature(temp: Int) = when (temp) {
in Int.MIN_VALUE..0 -> "Freezing"
in 1..20 -> "Cold"
in 21..30 -> "Moderate"
in 31..Int.MAX_VALUE -> "Hot"
else -> "Invalid"
}
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<String>) : UiState()
data class Error(val message: String) : UiState()
object Empty : UiState()
}
@Composable
fun UserListScreen(uiState: UiState) {
when (uiState) {
UiState.Loading -> LoadingIndicator()
is UiState.Success -> LazyColumn {
items(uiState.data) { item ->
Text(text = item)
}
}
is UiState.Error -> ErrorMessage(uiState.message)
UiState.Empty -> EmptyStateMessage()
}
}
data class ApiResponse<T>(
val statusCode: Int,
val data: T?,
val error: String?
)
fun <T> handleApiResponse(response: ApiResponse<T>) = when (response.statusCode) {
in 200..299 -> Result.success(response.data!!)
401 -> Result.failure(Exception("Unauthorized"))
in 400..499 -> Result.failure(Exception("Client error: ${response.error}"))
in 500..599 -> Result.failure(Exception("Server error: ${response.error}"))
else -> Result.failure(Exception("Unknown error"))
}
Here’s a comprehensive example demonstrating multiple when expression features in a real-world Android context:
import kotlin.random.Random
// Sealed class for different user types
sealed class UserType {
data class Premium(val subscriptionLevel: Int) : UserType()
data class Regular(val registrationDays: Int) : UserType()
object Guest : UserType()
data class Admin(val permissions: List<String>) : UserType()
}
// Data class for app features
data class AppFeature(val name: String, val requiresPremium: Boolean, val minimumLevel: Int = 1)
class FeatureAccessManager {
private val features = listOf(
AppFeature("basic_chat", requiresPremium = false),
AppFeature("video_call", requiresPremium = true, minimumLevel = 2),
AppFeature("cloud_storage", requiresPremium = true, minimumLevel = 1),
AppFeature("advanced_analytics", requiresPremium = true, minimumLevel = 3),
AppFeature("admin_panel", requiresPremium = false) // Admin only
)
fun checkFeatureAccess(userType: UserType, featureName: String): AccessResult {
val feature = features.find { it.name == featureName }
?: return AccessResult.FeatureNotFound
return when (userType) {
is UserType.Premium -> when {
feature.name == "admin_panel" -> AccessResult.Denied("Admin access required")
feature.requiresPremium && userType.subscriptionLevel >= feature.minimumLevel ->
AccessResult.Granted
!feature.requiresPremium -> AccessResult.Granted
else -> AccessResult.Denied("Premium level ${feature.minimumLevel} required")
}
is UserType.Regular -> when {
feature.name == "admin_panel" -> AccessResult.Denied("Admin access required")
feature.requiresPremium -> AccessResult.Denied("Premium subscription required")
userType.registrationDays >= 7 -> AccessResult.Granted
else -> AccessResult.Denied("Account must be 7+ days old")
}
UserType.Guest -> when (feature.name) {
"basic_chat" -> AccessResult.GrantedWithLimits("10 messages per day")
else -> AccessResult.Denied("Registration required")
}
is UserType.Admin -> when {
userType.permissions.contains("ALL") -> AccessResult.Granted
userType.permissions.contains(feature.name) -> AccessResult.Granted
else -> AccessResult.Denied("Insufficient admin permissions")
}
}
}
fun generateWelcomeMessage(userType: UserType): String = when (userType) {
is UserType.Premium -> when (userType.subscriptionLevel) {
in 1..2 -> "Welcome, Premium user! Enjoy your enhanced features."
in 3..5 -> "Welcome, Premium Pro! Access to all premium features unlocked."
else -> "Welcome, Premium Elite! You have unlimited access."
}
is UserType.Regular -> "Welcome back! ${
when {
userType.registrationDays < 7 -> "Complete verification to unlock more features."
userType.registrationDays < 30 -> "You're doing great! Consider upgrading to Premium."
else -> "Thanks for being a loyal user! Special offers await."
}
}"
UserType.Guest -> "Welcome, Guest! Sign up to unlock amazing features."
is UserType.Admin -> "Welcome, Administrator. System status: ${getSystemStatus()}"
}
private fun getSystemStatus(): String = when (Random.nextInt(1, 4)) {
1 -> "All systems operational"
2 -> "Minor maintenance in progress"
else -> "System monitoring active"
}
}
// Sealed class for access results
sealed class AccessResult {
object Granted : AccessResult()
data class GrantedWithLimits(val limitations: String) : AccessResult()
data class Denied(val reason: String) : AccessResult()
object FeatureNotFound : AccessResult()
}
// Usage example
fun main() {
val accessManager = FeatureAccessManager()
// Test different user types
val users = listOf(
UserType.Premium(subscriptionLevel = 3),
UserType.Regular(registrationDays = 15),
UserType.Guest,
UserType.Admin(permissions = listOf("basic_chat", "video_call", "admin_panel"))
)
val testFeatures = listOf("basic_chat", "video_call", "cloud_storage", "admin_panel")
users.forEach { user ->
println("\n--- ${user::class.simpleName} ---")
println(accessManager.generateWelcomeMessage(user))
testFeatures.forEach { feature ->
val result = accessManager.checkFeatureAccess(user, feature)
val status = when (result) {
AccessResult.Granted -> "✅ GRANTED"
is AccessResult.GrantedWithLimits -> "⚠️ GRANTED (${result.limitations})"
is AccessResult.Denied -> "❌ DENIED (${result.reason})"
AccessResult.FeatureNotFound -> "❓ FEATURE NOT FOUND"
}
println("$feature: $status")
}
}
}
Output:
--- Premium ---
Welcome, Premium Pro! Access to all premium features unlocked.
basic_chat: ✅ GRANTED
video_call: ✅ GRANTED
cloud_storage: ✅ GRANTED
admin_panel: ❌ DENIED (Admin access required)
--- Regular ---
Welcome back! You're doing great! Consider upgrading to Premium.
basic_chat: ✅ GRANTED
video_call: ❌ DENIED (Premium subscription required)
cloud_storage: ❌ DENIED (Premium subscription required)
admin_panel: ❌ DENIED (Admin access required)
--- Guest ---
Welcome, Guest! Sign up to unlock amazing features.
basic_chat: ⚠️ GRANTED (10 messages per day)
video_call: ❌ DENIED (Registration required)
cloud_storage: ❌ DENIED (Registration required)
admin_panel: ❌ DENIED (Registration required)
--- Admin ---
Welcome, Administrator. System status: All systems operational
basic_chat: ✅ GRANTED
video_call: ✅ GRANTED
cloud_storage: ❌ DENIED (Insufficient admin permissions)
admin_panel: ✅ GRANTED
This comprehensive example demonstrates the kotlin when expression in action, showcasing its power for building robust, type-safe conditional logic in Android applications. The pattern matching capabilities, combined with sealed classes and smart casting, create clean, maintainable code that’s both expressive and performant.