Kotlin Tutoial

Basics

Control Flow

Array & String

Functions

Collections

OOPs Concept

Exception Handling

Null Safety

Regex & Ranges

Java Interoperability

Miscellaneous

Android

Kotlin | Type Checking and Smart Casting

In Kotlin, type checking and smart casting simplify the process of checking types and casting variables, leading to more concise and readable code. In this tutorial, we'll dive into these concepts.

1. Type Checking: is and !is

The is keyword is used to check if a variable belongs to a particular type. The !is keyword checks if a variable does not belong to a type.

fun getType(input: Any) {
    when(input) {
        is String -> println("It's a String!")
        is Int -> println("It's an Integer!")
        is Double -> println("It's a Double!")
        else -> println("Unknown type!")
    }
}

In the example above, we're checking the type of input using the is keyword inside a when expression.

2. Smart Casting

One of the key features of Kotlin is smart casting. Once you check a variable's type, Kotlin automatically casts it to that type within the scope of that check. This means you don't have to do explicit casting as you often would in languages like Java.

fun getStringLength(input: Any): Int? {
    if (input is String) {
        // input is automatically cast to String within this scope
        return input.length
    }
    return null
}

In the function above, after we check if input is a String, we can directly call input.length without any explicit casting. Kotlin understands that within that block, input can only be a String.

3. as Operator for Casting

If you're sure about the type of a variable and want to cast it, you can use the as keyword. However, be careful: if the type is not what you expect, a ClassCastException will be thrown.

val obj: Any = "Hello"
val str: String = obj as String

For safe casting, you can use as?, which returns null if the casting isn't possible:

val obj: Any = 123
val str: String? = obj as? String  // str will be null, no exception thrown

4. Limitations of Smart Casting

Smart casting works in most cases, but there are scenarios where Kotlin cannot smart cast. This is mainly in cases where the compiler cannot guarantee the variable hasn't changed its type between the check and the usage. This can happen with:

  • var properties - Since they can be changed by other code.
  • Mutable properties in classes - Their types could be changed by subclasses or external code.
  • Open properties in classes - Subclasses can override their behavior.

In such cases, you'll need to use explicit casting or other strategies.

Conclusion

Type checking and smart casting in Kotlin are powerful features that result in cleaner, more concise, and safer code. They simplify many common patterns you'll encounter and help prevent type-related errors. Always remember to be cautious with explicit casting, especially in scenarios where you're unsure of the variable's actual type.

  1. Using 'is' operator for type checking in Kotlin:

    • The is operator checks if an object is an instance of a particular type.
    val result: Any = "Hello, Kotlin!"
    if (result is String) {
        println(result.length)
    }
    
    
  2. Checking and casting types with 'as' operator in Kotlin:

    • The as operator is used for explicit casting.
    val anyValue: Any = "Kotlin"
    val stringValue: String = anyValue as String
    
  3. Smart casting and automatic type conversion in Kotlin:

    • Kotlin smart casts automatically cast a variable after a type check.
    fun printLength(value: Any) {
        if (value is String) {
            println(value.length) // Smart cast to String
        }
    }
    
  4. Working with nullable types and type checking in Kotlin:

    • Handle nullable types with safe casts using as?.
    val nullableValue: String? = "Nullable"
    val length: Int? = nullableValue as? String)?.length
    
  5. Combining type checking and when expressions in Kotlin:

    • Use when expressions for concise type checking.
    fun printType(value: Any) {
        when (value) {
            is String -> println("String")
            is Int -> println("Int")
            else -> println("Unknown Type")
        }
    }
    
  6. Type checking in Kotlin and polymorphism:

    • Leverage type checking for polymorphic behavior.
    interface Shape
    class Circle : Shape
    
    fun processShape(shape: Shape) {
        if (shape is Circle) {
            // Handle Circle-specific logic
        }
    }
    
  7. Type inference and smart casting in Kotlin:

    • Kotlin's type inference and smart casting work together for concise code.
    val value: Any = "Inferred Type"
    if (value is String) {
        println(value.length) // Smart cast to String
    }
    
  8. Custom type checking functions in Kotlin:

    • Create custom functions for type checking.
    fun isEven(number: Int): Boolean = number % 2 == 0
    if (isEven(4)) {
        // Handle even number logic
    }
    
  9. Type checking with sealed classes in Kotlin:

    • Sealed classes facilitate exhaustive type checking.
    sealed class Result
    class Success : Result()
    class Error : Result()
    
    fun processResult(result: Result) {
        when (result) {
            is Success -> println("Success")
            is Error -> println("Error")
        }
    }
    
  10. Type checking and extension functions in Kotlin:

    • Extend existing types with type-specific functions.
    fun String.isPalindrome(): Boolean {
        return this == this.reversed()
    }
    
    val palindromeCheck = "radar".isPalindrome()
    
  11. Type checking and reflection in Kotlin:

    • Reflection allows runtime type information.
    val obj: Any = "Reflection"
    val className = obj::class.simpleName
    println("Type: $className")