Kotlin Expressions

A Kotlin expression is any piece of code that evaluates to a value. This fundamental concept sets Kotlin apart from many other programming languages. In Kotlin, expressions are the building blocks that combine variables, operators, method calls, and literals to produce a single result value.

Every Kotlin expression has a type and returns a value, even if that value is Unit (equivalent to void in Java). This expression-oriented approach makes Kotlin more functional and allows for more concise code patterns.

Key characteristics of Kotlin expressions:

  • Always return a value
  • Can be assigned to variables
  • Can be used as function arguments
  • Can contain other expressions (nested expressions)
  • Have a specific type determined at compile time

Here’s a simple example of a Kotlin expression:

val result = 10 + 5  // 10 + 5 is an expression that evaluates to 15  

Kotlin Expressions vs Statements: Understanding the Difference

The distinction between Kotlin expressions and statements is crucial for understanding how Kotlin code works. While expressions always return values, statements perform actions without returning meaningful values.

Kotlin expressions:

  • Return a value that can be used
  • Can be assigned to variables
  • Can be passed as function parameters
  • Examples: 5 + 3, if (a > b) a else b, when(x) { 1 -> "one" else -> "other" }

Kotlin statements:

  • Perform actions or declare something
  • Don’t return usable values
  • Examples: variable declarations (val x = 5), function declarations
// Expression - returns a value  
val maximum = if (a > b) a else b  
  
// Statement - declares a variable  
val number = 42  
  
// Expression used as statement  
println("Hello")  // Function call expression used as statement  

Types of Kotlin Expressions

1. Arithmetic Expressions

Arithmetic Kotlin expressions combine numbers using mathematical operators. These expressions follow standard mathematical precedence rules.

val addition = 10 + 5        // Addition expression  
val subtraction = 20 - 8     // Subtraction expression    
val multiplication = 4 * 7   // Multiplication expression  
val division = 15 / 3        // Division expression  
val modulus = 17 % 5         // Modulus expression  
val power = 2.0.pow(3.0)     // Power expression (requires import)  

Compound arithmetic expressions combine multiple operations:

val complexCalculation = (10 + 5) * 3 - 8 / 2  // Evaluates to 41  
val averageScore = (math + science + english) / 3  

2. Boolean Expressions

Boolean Kotlin expressions evaluate to either true or false. These are essential for conditional logic and control flow.

val isAdult = age >= 18                    // Comparison expression  
val isValidUser = isLoggedIn && isActive   // Logical AND expression  
val shouldShowAd = isPremium || hasTrialExpired  // Logical OR expression  
val isNotEmpty = !text.isEmpty()           // Logical NOT expression  

Complex boolean expressions can combine multiple conditions:

val canAccessFeature = (user.isPremium || user.isAdmin) &&   
                      user.isEmailVerified &&   
                      !user.isSuspended  

3. If Expressions

One of the most powerful features of Kotlin is that if constructs are expressions, not just statements. Kotlin if expressions can return values, making them incredibly useful for conditional assignments.

// Simple if expression  
val status = if (score >= 90) "Excellent" else "Good"  
  
// Multi-line if expression  
val grade = if (percentage >= 90) {  
    println("Outstanding performance!")  
    "A+"  
} else if (percentage >= 80) {  
    println("Great job!")  
    "A"  
} else {  
    println("Keep trying!")  
    "B"  
}  

If expressions must have an else branch when used as expressions because every expression must return a value.

// This won't compile - missing else branch  
// val result = if (condition) "yes"  // Error!  
  
// Correct version  
val result = if (condition) "yes" else "no"  

4. When Expressions

Kotlin when expressions are the modern replacement for Java’s switch statements, but far more powerful. They can work with any type and support complex pattern matching.

// Basic when expression  
val dayType = when (dayOfWeek) {  
    1, 2, 3, 4, 5 -> "Weekday"  
    6, 7 -> "Weekend"  
    else -> "Invalid day"  
}  
  
// When expression with ranges  
val ageGroup = when (age) {  
    in 0..12 -> "Child"  
    in 13..19 -> "Teenager"  
    in 20..64 -> "Adult"  
    in 65..120 -> "Senior"  
    else -> "Invalid age"  
}  
  
// When expression with type checking  
val description = when (value) {  
    is String -> "Text: ${value.length} characters"  
    is Int -> "Number: $value"  
    is Boolean -> "Boolean: $value"  
    else -> "Unknown type"  
}  

When expressions without arguments act like if-else chains:

val recommendation = when {  
    temperature > 30 -> "It's hot, stay hydrated"  
    temperature > 20 -> "Perfect weather for outdoor activities"  
    temperature > 10 -> "A bit cool, wear a light jacket"  
    else -> "It's cold, dress warmly"  
}  

5. Lambda Expressions

Kotlin lambda expressions are anonymous functions that can be treated as values. They’re essential for functional programming and working with collections.

// Basic lambda expression  
val square = { x: Int -> x * x }  
val result = square(5)  // Returns 25  
  
// Lambda with multiple parameters  
val multiply = { a: Int, b: Int -> a * b }  
val product = multiply(4, 7)  // Returns 28  
  
// Lambda with collections  
val numbers = listOf(1, 2, 3, 4, 5)  
val doubled = numbers.map { it * 2 }  // [2, 4, 6, 8, 10]  
val evens = numbers.filter { it % 2 == 0 }  // [2, 4]  

Higher-order function expressions with lambdas:

// Function that takes a lambda as parameter  
fun processNumbers(numbers: List<Int>, operation: (Int) -> Int): List<Int> {  
    return numbers.map(operation)  
}  
  
// Using the function with lambda expressions  
val squares = processNumbers(listOf(1, 2, 3, 4)) { it * it }  
val cubes = processNumbers(listOf(1, 2, 3, 4)) { it * it * it }  

6. Try-Catch Expressions

Kotlin try-catch expressions can return values, making error handling more functional and expressive.

// Try expression returning a value  
val number = try {  
    inputString.toInt()  
} catch (e: NumberFormatException) {  
    0  // Default value if parsing fails  
}  
  
// Try expression with complex logic  
val result = try {  
    val data = fetchDataFromAPI()  
    processData(data)  
    "Success"  
} catch (e: NetworkException) {  
    "Network error: ${e.message}"  
} catch (e: Exception) {  
    "Unknown error occurred"  
} finally {  
    cleanup()  // Finally block doesn't affect the returned value  
}  

7. Function Call Expressions

Every function call in Kotlin is an expression that returns a value, even if that value is Unit.

// Function that returns a value  
fun calculateArea(radius: Double): Double = Math.PI * radius * radius  
val area = calculateArea(5.0)  // Function call expression  
  
// Function with Unit return type  
fun printMessage(msg: String): Unit = println(msg)  
printMessage("Hello World")  // Expression returning Unit  
  
// Expression function (single expression function)  
fun getGreeting(name: String) = "Hello, $name!"  
val greeting = getGreeting("Alice")  

8. String Template Expressions

Kotlin string template expressions allow embedding expressions directly within strings using the $ symbol.

val name = "John"  
val age = 25  
  
// Simple variable interpolation  
val introduction = "My name is $name and I am $age years old"  
  
// Complex expression interpolation  
val message = "Next year, $name will be ${age + 1} years old"  
  
// Function call in string template  
val formatted = "Current time: ${getCurrentTime()}"  
  
// Expression with string operations  
val details = "Name length: ${name.length}, uppercase: ${name.uppercase()}"  

9. Object and Array Access Expressions

Accessing properties and array elements are also expressions in Kotlin.

// Property access expressions  
val person = Person("Alice", 30)  
val personName = person.name     // Property access expression  
val personAge = person.age       // Property access expression  
  
// Array access expressions  
val numbers = arrayOf(10, 20, 30, 40, 50)  
val firstNumber = numbers[0]     // Array access expression  
val lastNumber = numbers[numbers.size - 1]  // Complex access expression  
  
// List access expressions  
val fruits = listOf("apple", "banana", "orange")  
val favoriteFruit = fruits[1]    // List access expression  
val fruitCount = fruits.size     // Property access expression  

Expression Bodies in Functions

Kotlin allows functions to have expression bodies instead of block bodies, making code more concise for simple functions.

// Traditional function with block body  
fun addTraditional(a: Int, b: Int): Int {  
    return a + b  
}  
  
// Function with expression body  
fun add(a: Int, b: Int): Int = a + b  
  
// Expression body with type inference  
fun multiply(a: Int, b: Int) = a * b  
  
// More complex expression body  
fun getDiscountedPrice(price: Double, discount: Double) =   
    if (discount > 0) price * (1 - discount) else price  

Nested Expressions and Complex Combinations

Kotlin expressions can be nested and combined to create sophisticated logic in a readable way.

// Nested expressions  
val complexResult = when {  
    temperature > 30 -> if (humidity > 80) "Hot and humid" else "Hot and dry"  
    temperature > 20 -> if (isRaining) "Warm but rainy" else "Pleasant weather"  
    else -> if (isSnowing) "Cold and snowy" else "Cold"  
}  
  
// Expression chains  
val processedData = inputList  
    .filter { it.isValid }  
    .map { it.process() }  
    .sortedBy { it.priority }  
    .take(10)  
  
// Combined expressions in function calls  
fun analyzeData(data: List<DataPoint>) = data  
    .groupBy { it.category }  
    .mapValues { (_, values) ->   
        values.map { it.value }.average()   
    }  
    .filterValues { it > threshold }  

Practical Examples and Use Cases

Example 1: Configuration Manager

Here’s a practical example using various Kotlin expressions in a configuration management scenario:

class ConfigurationManager(private val environment: String) {  
      
    // When expression for environment-specific settings  
    private val databaseUrl = when (environment.lowercase()) {  
        "development" -> "jdbc:h2:mem:devdb"  
        "testing" -> "jdbc:h2:mem:testdb"  
        "staging" -> "jdbc:postgresql://staging-db:5432/app"  
        "production" -> "jdbc:postgresql://prod-db:5432/app"  
        else -> throw IllegalArgumentException("Unknown environment: $environment")  
    }  
      
    // If expression for feature flags  
    private val enableCaching = if (environment == "production") true else false  
      
    // Try expression for safe property access  
    private val maxConnections = try {  
        System.getProperty("db.maxConnections")?.toInt()  
    } catch (e: NumberFormatException) {  
        null  
    } ?: getDefaultMaxConnections()  
      
    // Lambda expression for default calculation  
    private fun getDefaultMaxConnections() = when (environment) {  
        "production" -> 50  
        "staging" -> 20  
        else -> 10  
    }  
      
    // Expression function for configuration summary  
    fun getConfigSummary() = """  
        Environment: $environment  
        Database URL: $databaseUrl  
        Caching Enabled: $enableCaching  
        Max Connections: $maxConnections  
    """.trimIndent()  
}  

Example 2: Data Processing Pipeline

This example demonstrates Kotlin expressions in a data processing context:

data class SalesRecord(val product: String, val amount: Double, val region: String, val date: String)  
  
class SalesAnalyzer {  
      
    fun analyzeSales(records: List<SalesRecord>): Map<String, Double> {  
        // Complex expression chain for data analysis  
        return records  
            .filter { it.amount > 0 }  // Lambda expression  
            .groupBy { it.region }     // Lambda expression  
            .mapValues { (_, regionRecords) ->  // Lambda with destructuring  
                regionRecords  
                    .map { it.amount }  // Lambda expression  
                    .sum()              // Method call expression  
            }  
            .filterValues { it > 1000 }  // Lambda expression  
    }  
      
    fun getTopPerformingRegion(records: List<SalesRecord>): String {  
        // Nested expressions with elvis operator  
        return analyzeSales(records)  
            .maxByOrNull { it.value }  // Lambda expression  
            ?.key                      // Safe call expression  
            ?: "No data available"     // Elvis expression  
    }  
      
    fun generateReport(records: List<SalesRecord>): String {  
        val totalSales = records.sumOf { it.amount }  // Lambda expression  
        val avgSale = if (records.isNotEmpty()) totalSales / records.size else 0.0  
        val topRegion = getTopPerformingRegion(records)  
          
        // String template expressions  
        return """  
            Sales Report  
            ============  
            Total Records: ${records.size}  
            Total Sales: $${String.format("%.2f", totalSales)}  
            Average Sale: $${String.format("%.2f", avgSale)}  
            Top Region: $topRegion  
            Report Generated: ${getCurrentTimestamp()}  
        """.trimIndent()  
    }  
      
    private fun getCurrentTimestamp() = System.currentTimeMillis().toString()  
}  

Complete Working Example

Here’s a comprehensive example that demonstrates multiple types of Kotlin expressions working together:

import kotlin.math.pow  
import kotlin.random.Random  
  
// Data classes for the example  
data class User(val name: String, val age: Int, val isPremium: Boolean, val score: Double)  
data class GameResult(val user: User, val level: Int, val points: Int, val timeInSeconds: Int)  
  
class GameAnalytics {  
      
    // Expression function for user classification  
    fun classifyUser(user: User) = when {  
        user.age < 13 -> "Junior Player"  
        user.age < 18 -> "Teen Player"    
        user.age < 65 -> if (user.isPremium) "Premium Adult" else "Regular Adult"  
        else -> "Senior Player"  
    }  
      
    // Complex expression for score calculation  
    fun calculateFinalScore(result: GameResult): Double {  
        val baseScore = result.points.toDouble()  
          
        // Time bonus using if expression  
        val timeBonus = if (result.timeInSeconds < 60) {  
            baseScore * 0.5  // 50% bonus for quick completion  
        } else if (result.timeInSeconds < 120) {  
            baseScore * 0.25 // 25% bonus for moderate speed  
        } else {  
            0.0  // No time bonus  
        }  
          
        // Level multiplier using when expression  
        val levelMultiplier = when (result.level) {  
            in 1..5 -> 1.0  
            in 6..10 -> 1.5  
            in 11..15 -> 2.0  
            else -> 2.5  
        }  
          
        // Premium bonus using if expression  
        val premiumBonus = if (result.user.isPremium) baseScore * 0.1 else 0.0  
          
        // Final calculation expression  
        return (baseScore + timeBonus + premiumBonus) * levelMultiplier  
    }  
      
    // Expression for achievement determination  
    fun getAchievements(results: List<GameResult>): List<String> {  
        val achievements = mutableListOf<String>()  
          
        // Lambda expressions for data analysis  
        val totalGames = results.size  
        val avgScore = results.map { calculateFinalScore(it) }.average()  
        val maxLevel = results.maxOfOrNull { it.level } ?: 0  
        val fastestTime = results.minOfOrNull { it.timeInSeconds } ?: Int.MAX_VALUE  
          
        // When expressions for achievement logic  
        when {  
            totalGames >= 100 -> achievements.add("Century Player")  
            totalGames >= 50 -> achievements.add("Dedicated Gamer")  
            totalGames >= 10 -> achievements.add("Regular Player")  
        }  
          
        when {  
            avgScore >= 10000 -> achievements.add("Score Master")  
            avgScore >= 5000 -> achievements.add("High Scorer")  
            avgScore >= 1000 -> achievements.add("Good Player")  
        }  
          
        when {  
            maxLevel >= 20 -> achievements.add("Level Champion")  
            maxLevel >= 15 -> achievements.add("Advanced Player")  
            maxLevel >= 10 -> achievements.add("Intermediate Player")  
        }  
          
        when {  
            fastestTime < 30 -> achievements.add("Speed Demon")  
            fastestTime < 60 -> achievements.add("Quick Player")  
        }  
          
        return achievements  
    }  
      
    // String template expressions for reporting  
    fun generatePlayerReport(user: User, results: List<GameResult>): String {  
        val classification = classifyUser(user)  
        val totalScore = results.sumOf { calculateFinalScore(it) }  
        val achievements = getAchievements(results)  
        val bestResult = results.maxByOrNull { calculateFinalScore(it) }  
          
        return """  
            Player Report for ${user.name}  
            ==============================  
            Classification: $classification  
            Total Games Played: ${results.size}  
            Total Score: ${String.format("%.2f", totalScore)}  
            Average Score: ${String.format("%.2f", if (results.isNotEmpty()) totalScore / results.size else 0.0)}  
            Best Game Score: ${bestResult?.let { String.format("%.2f", calculateFinalScore(it)) } ?: "N/A"}  
            Highest Level Reached: ${results.maxOfOrNull { it.level } ?: 0}  
            Achievements: ${if (achievements.isNotEmpty()) achievements.joinToString(", ") else "None yet"}  
            Premium Status: ${if (user.isPremium) "Active" else "Not Active"}  
        """.trimIndent()  
    }  
}  
  
// Main function demonstrating the usage  
fun main() {  
    // Sample data creation using expressions  
    val users = listOf(  
        User("Alice", 25, true, 85.5),  
        User("Bob", 16, false, 72.0),  
        User("Charlie", 67, true, 90.0)  
    )  
      
    val gameResults = listOf(  
        GameResult(users[0], 12, 1500, 45),  
        GameResult(users[0], 15, 2200, 75),  
        GameResult(users[1], 8, 900, 120),  
        GameResult(users[2], 20, 3000, 30)  
    )  
      
    val analytics = GameAnalytics()  
      
    // Using expressions to generate reports  
    users.forEach { user ->  
        val userResults = gameResults.filter { it.user == user }  
        if (userResults.isNotEmpty()) {  
            println(analytics.generatePlayerReport(user, userResults))  
            println("\n" + "=".repeat(50) + "\n")  
        }  
    }  
      
    // Additional analysis using expressions  
    val topScorer = gameResults  
        .map { it.user to analytics.calculateFinalScore(it) }  
        .maxByOrNull { it.second }  
        ?.first  
      
    println("Top scorer: ${topScorer?.name ?: "Unknown"}")  
      
    // Complex expression for statistics  
    val statisticsSummary = gameResults  
        .groupBy { it.user.name }  
        .mapValues { (_, results) ->  
            results.map { analytics.calculateFinalScore(it) }.average()  
        }  
        .entries  
        .sortedByDescending { it.value }  
        .joinToString("\n") { "${it.key}: ${String.format("%.2f", it.value)}" }  
      
    println("\nPlayer Rankings by Average Score:")  
    println(statisticsSummary)  
}  

Output

When you run the complete example above, you’ll see output similar to this:

Player Report for Alice  
==============================  
Classification: Premium Adult  
Total Games Played: 2  
Total Score: 8775.00  
Average Score: 4387.50  
Best Game Score: 4950.00  
Highest Level Reached: 15  
Achievements: Regular Player, High Scorer, Advanced Player  
Premium Status: Active  
  
==================================================  
  
Player Report for Bob  
==============================  
Classification: Teen Player  
Total Games Played: 1  
Total Score: 1350.00  
Average Score: 1350.00  
Best Game Score: 1350.00  
Highest Level Reached: 8  
Achievements: Good Player  
Premium Status: Not Active  
  
==================================================  
  
Player Report for Charlie  
==============================  
Classification: Senior Player  
Total Games Played: 1  
Total Score: 9075.00  
Average Score: 9075.00  
Best Game Score: 9075.00  
Highest Level Reached: 20  
Achievements: High Scorer, Level Champion, Speed Demon  
Premium Status: Active  
  
==================================================  
  
Top scorer: Charlie  
  
Player Rankings by Average Score:  
Charlie: 9075.00  
Alice: 4387.50  
Bob: 1350.00