Question
How to Check Type at Runtime in Kotlin: instanceof, is, and Smart Casts
Question
In Kotlin, I have a method that accepts a parameter typed as a general object, and I may pass instances of different classes when calling it.
In Java, I can use instanceof to check what class an object belongs to at runtime.
How can I do the same kind of runtime type check in Kotlin? What is the Kotlin equivalent of instanceof?
Short Answer
By the end of this page, you will understand how Kotlin checks an object's type at runtime using is and !is, how smart casts work after a successful type check, and when to use when for handling multiple possible types. You will also see common mistakes, practical examples, and how this pattern is used in real Kotlin code.
Concept
Kotlin does not use the Java keyword instanceof. Instead, Kotlin uses the is operator for runtime type checks.
For example:
if (value is String) {
println("This is a String")
}
This checks whether value is an instance of String at runtime.
Kotlin also provides !is, which means “is not of this type”:
if (value !is String) {
println("This is not a String")
}
Why this matters
In real programs, you often receive values whose exact type is not known in advance:
- function parameters typed as
Any - API responses mapped into different models
- sealed class hierarchies
- event systems
- error objects
Runtime type checking lets you safely decide what to do based on the actual object type.
A key Kotlin feature: smart casts
One major improvement over Java is that after checking with is, Kotlin can automatically treat the variable as that type inside the safe branch. This is called a smart cast.
fun printLength(value: Any) {
if (value is String) {
println(value.length)
}
}
Inside the if block, value is automatically treated as a String, so you can access length directly without manually casting.
Kotlin's root object type
If you want a parameter that can accept any non-null object, use Any:
fun process(value: Any) {
// value can be many types
}
If the value can also be null, use Any?:
fun process(value: Any?) {
// value may be null
}
This is important because Kotlin does not use Java's Object in normal Kotlin code. The idiomatic Kotlin equivalent is Any.
Mental Model
Think of is as checking a label on a package before opening it.
Anymeans “this is some package, but we do not yet know what is inside.”is Stringmeans “check whether the package is labeled String.”- If the label matches, Kotlin lets you open it as that type automatically.
So instead of saying, “I know this is probably a String, let me cast it,” Kotlin says, “First verify the label, then I will safely treat it as a String for you.”
That automatic trust after the check is the smart cast.
Syntax and Examples
Basic syntax
if (value is String) {
println("value is a String")
}
if (value !is String) {
println("value is not a String")
}
Example with Any
fun describe(value: Any) {
if (value is Int) {
println("Integer: $value")
} else if (value is String) {
println("String with length ${value.length}")
} else {
println("Unknown type")
}
}
Explanation
valueis declared asAny, so it can hold values of many types.value is Intchecks whether the runtime object is anInt.value is Stringchecks whether it is aString.- Inside the
Stringbranch, Kotlin smart castsvaluetoString, sovalue.lengthworks.
Using when for multiple types
fun describe(value: Any) {
when (value) {
is Int -> println("Integer: $value")
is String -> println("String length: ${value.length}")
is Double -> println("Double: $value")
else -> println("Unknown type")
}
}
This is often cleaner than many if/else if checks.
Example with custom classes
open class Animal
class Dog(val name: String) : Animal()
class Cat(val age: Int) : Animal()
fun handleAnimal(animal: Animal) {
when (animal) {
is Dog -> println("Dog name: ${animal.name}")
is Cat -> println("Cat age: ${animal.age}")
}
}
Here, Kotlin checks the actual subclass at runtime and smart casts accordingly.
Step by Step Execution
Consider this example:
fun printInfo(value: Any) {
if (value is String) {
println("Text: ${value.uppercase()}")
} else {
println("Not a string")
}
}
fun main() {
printInfo("kotlin")
printInfo(42)
}
Step by step
First call: printInfo("kotlin")
- The argument is the string
"kotlin". - Inside
printInfo,valuehas declared typeAny. - Kotlin evaluates
value is String. - The check is
true. - Kotlin smart casts
valuetoStringinside theifblock. value.uppercase()runs and produces"KOTLIN".- Output:
Text: KOTLIN
Second call: printInfo(42)
- The argument is the integer
42. valueagain has declared typeAny.- Kotlin evaluates
value is String. - The check is
false. - The
elseblock runs. - Output:
Not a string
Final output
Text: KOTLIN
Not a string
Real World Use Cases
1. Handling mixed input values
A function may receive values from different sources:
fun logValue(value: Any) {
when (value) {
is String -> println("Message: $value")
is Int -> println("Count: $value")
is Boolean -> println("Enabled: $value")
}
}
2. Working with API or parsing results
After parsing data, the result may be one of several model types:
when (result) {
is Success -> println(result.data)
is Error -> println(result.message)
}
3. UI event handling
Applications often process different event objects:
when (event) {
is ClickEvent -> handleClick(event)
is ScrollEvent -> handleScroll(event)
}
4. Error handling
You may inspect exception types:
catch (e: Exception) {
when (e) {
is IllegalArgumentException -> println("Bad input")
is IllegalStateException -> println("Bad state")
}
}
5. Domain models with inheritance
If multiple classes share a parent class or interface, runtime checking helps select the correct behavior.
Real Codebase Usage
In real Kotlin projects, developers often use type checks in a few common patterns.
Guard clauses
Exit early if a value is not the expected type:
fun process(value: Any) {
if (value !is String) return
println(value.length)
}
This keeps the rest of the function clean.
when for branching by type
fun render(item: Any) {
when (item) {
is String -> println(item)
is Int -> println(item * 2)
else -> println("Unsupported")
}
}
Validation before using data
fun save(input: Any) {
require(input is String) { "Input must be a String" }
println(input.length)
}
Working with interfaces and polymorphism
interface PaymentMethod
class Card(val number: String) : PaymentMethod
class Cash(val amount: Double) : PaymentMethod
fun handlePayment(method: PaymentMethod) {
when (method) {
is Card -> println(method.number)
is Cash -> println(method.amount)
}
}
Prefer design over excessive type checks
In many codebases, if you are checking types very often, it may be a sign that polymorphism, interfaces, or sealed classes would make the code cleaner. Type checks are useful, but they should not replace good object-oriented design.
Common Mistakes
1. Using instanceof in Kotlin
This is Java syntax, not Kotlin syntax.
Wrong
if (value instanceof String) {
println(value)
}
Correct
if (value is String) {
println(value)
}
2. Using Object instead of Any
In Kotlin, the usual root type is Any, not Java's Object.
Better
fun process(value: Any) {
println(value)
}
3. Forgetting about nullability
Any cannot hold null. Use Any? if null is possible.
fun process(value: Any?) {
if (value is String) {
println(value.length)
}
}
This works because null is String is false.
4. Casting without checking
Risky
val text = value as String
println(text.length)
If value is not a String, this throws an exception.
Safer
if (value is String) {
println(value.length)
}
Or use a safe cast:
val text = value as? String
println(text?.length)
5. Expecting smart casts in every situation
Smart casts may not work if Kotlin cannot guarantee the variable stays unchanged.
Example
var value: Any = "hello"
if (value is String) {
println(value.length)
}
This often works in simple cases, but with mutable properties or more complex contexts, Kotlin may refuse the smart cast because the value could change.
6. Comparing exact class when you really want inheritance behavior
is checks whether an object is of a type or a subtype. That is usually what you want.
If you only want the exact runtime class, that is a different check and is less common in idiomatic Kotlin.
Comparisons
is vs Java instanceof
| Feature | Kotlin | Java |
|---|---|---|
| Type check operator | is | instanceof |
| Negative check | !is | !(x instanceof Type) |
| Automatic cast after check | Yes, smart cast | No, usually manual cast needed |
is vs as
| Operator | Purpose | Safe? | Result |
|---|---|---|---|
is | Check type | Yes | true or false |
as | Force cast | No | Casted value or exception |
as? | Safe cast | Yes | Casted value or null |
Example
if (value is String) {
println(value.length)
}
val text1 = value as String // may crash
val text2 = value as? String // returns null if not String
if vs when for type checks
| Option | Best for |
|---|---|
if | One simple type check |
when | Multiple possible types |
Any vs Any?
| Type | Can be null? | Use when |
|---|---|---|
Any | No | You expect a non-null object |
Any? | Yes | The value may be null |
Cheat Sheet
Runtime type checking in Kotlin
if (value is String) { }
if (value !is String) { }
Smart cast
if (value is String) {
println(value.length)
}
After a successful is check, Kotlin often smart casts the value.
Multiple types with when
when (value) {
is String -> println(value.length)
is Int -> println(value)
else -> println("Unknown")
}
Safe cast
val text = value as? String
println(text?.length)
Force cast
val text = value as String
Use carefully: this throws an exception if the type is wrong.
Root object types
Any // non-null any object
Any? // nullable any object
Rules to remember
- Kotlin uses
is, notinstanceof - Kotlin uses
Any, notObject, in normal Kotlin code isincludes subclasses toonull is SomeTypeisfalse- Prefer
whenwhen checking several types
FAQ
What is the Kotlin equivalent of instanceof?
Use the is operator.
if (value is String) {
println("It is a String")
}
How do I check that an object is not a type in Kotlin?
Use !is.
if (value !is String) {
println("It is not a String")
}
What type should I use instead of Java Object in Kotlin?
Use Any for non-null values and Any? if null is allowed.
Does is also match subclasses?
Yes. If an object is an instance of a subclass, is returns true for the parent type or implemented interface.
Do I need to cast after using is?
Usually no. Kotlin often smart casts automatically inside the checked branch.
What is the difference between is and as in Kotlin?
ischecks typeascasts forcefullyas?casts safely and returnsnullon failure
Can I use when instead of many if checks?
Yes. when is often the cleanest way to handle multiple possible runtime types.
Mini Project
Description
Create a small Kotlin function that receives values of different runtime types and prints a useful message for each one. This demonstrates how to use is, when, and smart casts in a practical way, similar to handling mixed API results, events, or user input.
Goal
Build a type inspector that accepts a value of type Any? and handles null, strings, integers, and booleans safely.
Requirements
Requirement 1 Requirement 2 Requirement 3
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.