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 is Kotlin’s negated runtime type-checking operator. It evaluates to true if an object is not an instance of a specified type, and false if it is. While it is logically equivalent to applying the unary logical NOT operator to a parenthesized standard type-check (!(expression is Type)), !is is syntactically distinct and functions as a dedicated binary operator in Kotlin’s grammar.

Syntax

expression !is Type

Technical Mechanics

Runtime Type Identification (RTTI) The operator relies on RTTI to inspect the actual runtime class of the object referenced by the expression. It does not evaluate the static (compile-time) type of the variable, but rather the instance it points to in memory. Nullability Rules The !is operator inherently handles nullability based on the right-hand type declaration:
  • If the target Type is non-nullable (e.g., String), evaluating null !is String returns true.
  • If the target Type is nullable (e.g., String?), evaluating null !is String? returns false, because null is considered a valid instance of any nullable type.
val obj: Any? = null
val checkOne = obj !is String  // true
val checkTwo = obj !is String? // false
Type Erasure Constraints Because Kotlin targets the JVM (among other platforms), !is is subject to generic type erasure. Generally, you cannot use !is to check against a specific generic instantiation at runtime, requiring the use of star-projected types instead.
// Compiler Error: Cannot check for instance of erased type
val check = obj !is List<String> 

// Valid: Checks against the star-projected type
val validCheck = obj !is List<*> 
However, there are specific scenarios where !is can evaluate generic type arguments:
  1. Reified Type Parameters: When used inside an inline function against a reified type parameter, as the type is preserved at the call site.
  2. Statically Known Type Arguments: When the type arguments of the expression are already statically known by the compiler (e.g., evaluating collection !is List<String> when collection is statically typed as Collection<String>).
  3. Arrays: Array types retain their generic type parameters at runtime, allowing checks like obj !is Array<String>.

Control-Flow Analysis and Smart Casting

The Kotlin compiler’s control-flow analysis engine heavily utilizes the !is operator to perform smart casting. While the is operator typically smart-casts within the bounds of a conditional block, !is is used to trigger smart casting for the remainder of a scope via early exits (such as return or throw). If the compiler detects that execution only continues when !is evaluates to false, it automatically safely casts the variable to the target type for all subsequent instructions in that scope.
fun evaluate(obj: Any) {
    if (obj !is String) {
        // 'obj' remains 'Any' in this block
        return 
    }
    
    // Smart cast: 'obj' is statically treated as 'String' from this point forward
    // No explicit cast (obj as String) is required
    val length = obj.length 
}
Master Kotlin with Deep Grasping Methodology!Learn More