Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The is operator evaluates whether an object is an instance of a specified type at runtime. It performs runtime type identification (RTTI) and returns a Boolean. Its negated counterpart, !is, returns true if the object is not an instance of the specified type.
expression is Type
expression !is Type

Compiler Mechanics

Smart Casting When an is check evaluates to true, the Kotlin compiler performs data-flow analysis. If the compiler can guarantee that the variable will not be modified between the check and its subsequent usage, it automatically applies a smart cast. Within the scope where the condition holds true, the variable is treated as the target type, eliminating the need for the explicit cast operator (as).
fun process(obj: Any) {
    if (obj is String) {
        // obj is smart-cast to String; String properties are accessible
        print(obj.length) 
    }
    
    if (obj !is String) return
    // obj is smart-cast to String in the remaining scope
    print(obj.length)
}
when Expressions The is operator is natively integrated into when expressions as a branch condition. This provides an idiomatic mechanism for multi-way type checking and smart casting without requiring explicit if-else chains.
fun evaluate(obj: Any) = when (obj) {
    is String -> print("String length: ${obj.length}") // Smart-cast to String
    is Int -> print("Integer value: $obj")             // Smart-cast to Int
    !is Boolean -> print("Not a boolean")
    else -> print("Unknown type")
}
Nullability Resolution The is operator strictly adheres to Kotlin’s type system regarding nullability.
  • Checking against a non-nullable type (is Type) implicitly acts as a null check. If the operand is null, the expression evaluates to false.
  • Checking against a nullable type (is Type?) evaluates to true if the operand is null, because null is a valid instance of Type?.
val obj: Any? = null

val checkOne = obj is String  // Evaluates to false
val checkTwo = obj is String? // Evaluates to true

JVM Type Erasure Limitations

Because Kotlin compiles to the JVM (in its primary target), generic type arguments are generally erased at runtime. Consequently, the is operator cannot evaluate parameterized types with specific type arguments by default. To perform type checks on generic collections or classes, you must use star-projected types (*), which verify the base class but not the generic payload.
val list: Any = listOf(1, 2, 3)

// COMPILER ERROR: Cannot check for instance of erased type: List<Int>
val invalidCheck = list is List<Int> 

// VALID: Checks if the object implements the List interface
val validCheck = list is List<*> 
Exceptions to Type Erasure There are three specific scenarios where the compiler permits the is operator to evaluate exact generic type arguments:
  1. Reified Type Parameters: When using inline functions with reified type parameters, the compiler inlines the actual type at the call site, preserving the type information at runtime.
  2. Arrays: Arrays in Kotlin are reified on the JVM. They retain their component types at runtime, allowing direct type checks against specific array types.
  3. Statically Known Type Arguments: If the compiler’s static type checking already guarantees the generic type argument based on the variable’s declaration, it permits an is check on the base class.
// 1. Reified Type Parameters
inline fun <reified T> checkType(obj: Any): Boolean {
    return obj is T // VALID: T is reified
}

// 2. Arrays
val arr: Any = arrayOf(1, 2, 3)
val isIntArray = arr is Array<Int> // VALID: Arrays retain component types

// 3. Statically Known Type Arguments
val collection: Collection<Int> = setOf(1, 2, 3)
val isList = collection is List<Int> // VALID: <Int> is statically known from Collection<Int>
Master Kotlin with Deep Grasping Methodology!Learn More