Kotlin Null Safety and Exception Handling: Safe Calls, Elvis Operator & try-catch

1. What is null safety in Kotlin?

Null safety is a Kotlin feature that prevents null pointer exceptions at compile time by distinguishing nullable (?) and non-nullable types. It ensures variables are either guaranteed to hold a value or explicitly marked as potentially null, reducing runtime crashes.

Key concepts:

Use Case: Safely handling optional data (e.g., user input, API responses).

2. How do nullable and non-nullable types work?

3. Can you give an example of null safety?

fun main() {
    // Non-nullable type
    val name: String = "Krishna"  // OK
    // val name2: String = null  // Compile error: Type mismatch

    // Nullable type
    var email: String? = "[email protected]"  // OK
    email = null  // OK

    // Safe call operator (?.)
    println(email?.length)  // Outputs null if null

    // Elvis operator (?:)
    val safeLength = email?.length ?: 0
    println("Safe length: $safeLength")  // Outputs 0 if null

    // Not-null assertion (!! - use cautiously)
    // println(email!!.length)  // Throws NullPointerException if null

    // Let operator (let) for null-safe operations
    email?.let { println("Email is: $it") }  // Only executes if not null
}
      

Output:

16
Safe length: 16
Email is: [email protected]
      

Note:

4. What is exception handling in Kotlin?

Exception handling in Kotlin manages runtime errors using try-catch-finally, similar to Java but with more concise syntax. Exceptions are thrown with throw and caught to prevent crashes.

Key components:

Custom Exceptions: Extend Exception or RuntimeException.

Use Case: Gracefully handling invalid input or file errors.

5. How do try-catch-finally and custom exceptions work?

class InvalidSalaryException(message: String) : Exception(message)

fun calculateBonus(salary: Double?): Double {
    try {
        if (salary == null || salary < 0) {
            throw InvalidSalaryException("Salary cannot be null or negative")
        }
        return salary * 0.1
    } catch (e: InvalidSalaryException) {
        println("Error: ${e.message}")
        return 0.0
    } finally {
        println("Bonus calculation completed")
    }
}

fun main() {
    val bonus1 = calculateBonus(50000.0)
    println("Valid bonus: $bonus1")
    
    val bonus2 = calculateBonus(-100.0)
    println("Invalid bonus: $bonus2")
    
    val bonus3 = calculateBonus(null)
    println("Null bonus: $bonus3")
}
      

Output:

Bonus calculation completed
Valid bonus: 5000.0
Error: Salary cannot be null or negative
Bonus calculation completed
Invalid bonus: 0.0
Error: Salary cannot be null or negative
Bonus calculation completed
Null bonus: 0.0
      

Note:

6. Can you provide a comprehensive example of null safety and exception handling?

class Employee {
    var name: String? = null
    var salary: Double? = null
    
    fun validateAndCalculateBonus(): Double? {
        try {
            val validName = name ?: throw IllegalArgumentException("Name cannot be null")
            val validSalary = salary ?: throw IllegalArgumentException("Salary cannot be null")
            
            if (validSalary < 0) {
                throw IllegalArgumentException("Salary must be positive")
            }
            
            return validSalary * 0.1  // 10% bonus
        } catch (e: IllegalArgumentException) {
            println("Validation error: ${e.message}")
            return null
        } catch (e: Exception) {
            println("Unexpected error: ${e.message}")
            return null
        } finally {
            println("Employee validation completed")
        }
    }
    
    fun safePrintInfo() {
        val safeName = name ?: "Unknown"
        val safeSalary = salary ?: 0.0
        println("Employee: $safeName, Salary: $${safeSalary}")
    }
}

fun main() {
    val emp1 = Employee()
    emp1.name = "Krishna"
    emp1.salary = 60000.0
    val bonus1 = emp1.validateAndCalculateBonus()
    println("Bonus for Krishna: $${bonus1 ?: 0.0}")
    emp1.safePrintInfo()
    
    val emp2 = Employee()
    emp2.name = "Ram"
    emp2.salary = -50000.0  // Invalid
    val bonus2 = emp2.validateAndCalculateBonus()
    println("Bonus for Ram: $${bonus2 ?: 0.0}")
    emp2.safePrintInfo()
    
    val emp3 = Employee()
    emp3.salary = 55000.0  // Name is null
    val bonus3 = emp3.validateAndCalculateBonus()
    println("Bonus for emp3: $${bonus3 ?: 0.0}")
    emp3.safePrintInfo()
}
      

Output:

Employee validation completed
Bonus for Krishna: $6000.0
Employee: Krishna, Salary: $60000.0
Validation error: Salary must be positive
Employee validation completed
Bonus for Ram: $0.0
Employee: Ram, Salary: $-50000.0
Validation error: Name cannot be null
Employee validation completed
Bonus for emp3: $0.0
Employee: Unknown, Salary: $55000.0
      

Description:

7. What are common mistakes in Kotlin null safety and exceptions?

Null Safety:

Exceptions:

General:

8. What are best practices for Kotlin null safety and exceptions?

Null Safety:

Exceptions:

General: