Question
In Java, I can determine a variable's runtime class by calling something.getClass(). In Kotlin, I know about something.javaClass, but I want the equivalent result as a KClass rather than a Java Class.
I have seen the Something::class syntax, but I need the KClass for the object currently stored in a variable. Does Kotlin provide this functionality?
Short Answer
Kotlin can obtain an object's runtime KClass with object::class. If you already have a Java Class, convert it with .kotlin, as in object.javaClass.kotlin. This page explains the difference between Kotlin and Java class representations, how runtime lookup works, and how to safely handle nullable values.
Concept
Kotlin has two closely related representations of class information:
KClass<T>is Kotlin's class metadata type, used by Kotlin reflection APIs.Class<T>is Java's class metadata type, used by Java reflection and JVM APIs.
For an object held by a variable, use:
val runtimeType = value::class
value::class evaluates the object at runtime and returns the KClass of the object's actual class. It is Kotlin's direct equivalent of Java's value.getClass(), except that its result is a KClass.
open class Animal
class Dog : Animal()
val pet: Animal = Dog()
println(pet::class) // class Dog
Although pet is declared as Animal, the object currently stored in it is a Dog. Therefore pet::class returns Dog::class.
On the JVM, Kotlin also exposes Java class metadata:
val javaType: Class<*> = pet.javaClass
val kotlinType: kotlin.reflect.KClass<*> = pet.javaClass.kotlin
Use ::class when you want Kotlin-oriented metadata. Use javaClass when an API expects java.lang.Class.
Mental Model
Think of a variable as a labelled parking space and an object as the vehicle parked in it.
- The variable's declared type is the sign above the space:
Animal. - The runtime object type is the vehicle actually parked there:
Dog.
pet::class looks at the vehicle currently in the space, so it reports Dog::class.
By contrast, Animal::class does not inspect a variable at all. It refers directly to the class named Animal, like reading a class label from a blueprint.
Syntax and Examples
Use expression::class to retrieve an object's runtime Kotlin class.
class User(val name: String)
val user = User("Mina")
val type = user::class
println(type) // class User
println(type.simpleName) // User
user is an expression containing an object. user::class returns a KClass<User>.
For a value whose declared type is a parent class or interface, the returned class still describes the concrete runtime object:
interface PaymentMethod
class CardPayment : PaymentMethod
val method: PaymentMethod = CardPayment()
println(method::class.simpleName) // CardPayment
You can also convert from a Java Class when needed:
val kotlinClass = method.javaClass.kotlin
println(kotlinClass.simpleName) // CardPayment
For normal non-null values, prefer method::class because it directly communicates that you want a Kotlin class reference.
Step by Step Execution
Consider this example:
open class Message
class EmailMessage : Message()
fun main() {
val message: Message = EmailMessage()
val type = message::class
println(type.simpleName)
}
Execution steps:
Messageis declared as a base class.EmailMessageinherits fromMessage.- The variable
messageis declared asMessage, so code can treat it as any kind of message. - An
EmailMessageobject is assigned tomessage. message::classinspects that object at runtime and producesEmailMessage::class.type.simpleNamereads the simple name from the resultingKClass.- The program prints
EmailMessage.
The declared type (Message) and runtime type (EmailMessage) can be different. ::class on an object reports the runtime type.
Real World Use Cases
Runtime KClass values are useful when code must inspect or route objects dynamically.
-
Logging and diagnostics: Include the runtime type in an error message.
fun logUnexpected(value: Any) { println("Unexpected value type: ${value::class.qualifiedName}") } -
Serialization and deserialization: A framework may select a serializer based on a value's runtime type.
-
Dependency injection: A container can use
KClasskeys to find registered services.val services: Map<kotlin.reflect.KClass<*>, Any> = mapOf( String::class to "configured value" ) -
Plugin or command systems: Determine what concrete implementation was supplied.
-
Validation and debugging: Detect unexpected values returned from an API or stored in a collection.
Do not use runtime class checks when regular polymorphism is sufficient. For example, calling message.send() is usually better than checking whether message::class == EmailMessage::class and branching manually.
Real Codebase Usage
In production Kotlin code, class references often appear in validation, registries, and boundaries between generic code and concrete implementations.
Validate a runtime type
When exact type identity matters, compare KClass values:
fun acceptsOnlyStrings(value: Any): Boolean {
return value::class == String::class
}
For inheritance-aware checks, use is instead:
fun printIfMessage(value: Any) {
if (value is Message) {
println("This is a message")
}
}
is is usually more readable and supports subclasses. Exact KClass comparison does not.
Use class references as registry keys
class ServiceRegistry {
private val services = mutableMapOf<kotlin.reflect.KClass<*>, Any>()
fun register(service: Any) {
services[service::class] = service
}
fun find(type: kotlin.reflect.KClass<*>): Any? = services[type]
}
Handle nullable inputs with a guard clause
A nullable value has no runtime class when it is null. Return early before reading ::class:
fun describe(value: Any?): String {
if (value == null) return "No value"
return "Runtime type: ${value::class.simpleName}"
}
This makes the non-null requirement explicit and prevents errors.
Common Mistakes
Confusing a class literal with an object's class
class User
val user = User()
println(User::class) // The KClass for the named User class
println(user::class) // The runtime KClass for the object in user
Both may produce the same result in this simple example, but they mean different things. User::class is known from the source code; user::class examines the value at runtime.
Calling ::class on a nullable value
This is unsafe because the value may be null:
val value: Any? = null
// val type = value::class // Not valid for a nullable receiver
Check for null first:
val type = value?.let { it::class }
println(type) // null
Expecting the declared type instead of the runtime type
open class Animal
class Cat : Animal()
val animal: Animal = Cat()
println(animal::class.simpleName) // Cat, not Animal
If you need the declared type, use the known class literal, such as Animal::class, rather than inspecting the object.
Mixing Class and KClass
The following types are not interchangeable:
val javaClass: Class<*> = "hello".javaClass
val kotlinClass: kotlin.reflect.KClass<*> = "hello"::class
Convert explicitly when required:
val asKClass = javaClass.kotlin
val asJavaClass = kotlinClass.java
Using class equality when is is the real requirement
// Too strict: does not allow subclasses of Animal
if (value::class == Animal::class) {
println("Animal")
}
Use an is check for a type family:
if (value is Animal) {
println("Animal or one of its subclasses")
}
Comparisons
| Expression | Result | Best use |
|---|---|---|
value::class | KClass<out T> for the runtime object | Kotlin reflection and runtime type reporting |
TypeName::class | KClass<TypeName> for a named type | Referencing a type known in source code |
value.javaClass | Java Class<out T> for the runtime object | Java APIs and Java reflection |
value.javaClass.kotlin | KClass<out T> | Converting an existing Java Class result |
type.java | Java Class<T> | Passing a KClass to a Java API |
value is TypeName | Boolean | Checking whether a value is a type or subtype |
::class versus is
Use ::class when you need the class object itself, perhaps to log it or use it as a map key.
Use is when you only need to make a type-based decision:
if (value is String) {
println(value.length)
}
is String supports smart casts. Comparing value::class == String::class does not provide the same convenient smart cast and is less flexible for subclasses.
Cheat Sheet
// Runtime KClass of a non-null object
val type = value::class
// KClass for a type written in source code
val type = String::class
// Runtime Java Class
val javaType = value.javaClass
// Convert Java Class to KClass
val kotlinType = value.javaClass.kotlin
// Convert KClass to Java Class
val javaType = String::class.java
// Safely get KClass from a nullable value
val typeOrNull = nullableValue?.let { it::class }
// Test a type or subtype; usually preferable for branching
if (value is String) {
println(value.length)
}
Key rules:
value::classreturns the object's runtimeKClass.NamedType::classrefers to a type explicitly named in code.nullhas no runtime class; handle it before using::class.- Use
isfor subtype-aware conditional logic. - Use
.javaand.kotlinto cross the Kotlin/Java reflection boundary.
FAQ
How do I get the KClass of an object in Kotlin?
Use object::class:
val type = object::class
It returns the runtime KClass of the non-null object.
Is object::class the Kotlin equivalent of Java getClass()?
Yes. object::class is the direct Kotlin-style equivalent when you want a KClass. object.javaClass is the closer equivalent when you want Java's Class type.
What is the difference between MyType::class and value::class?
MyType::class references the class named in your code. value::class examines the current runtime object stored in value.
How can I get a KClass from javaClass?
Use the .kotlin extension property:
val type = value.javaClass.kotlin
Can I call ::class on a nullable variable?
Not directly. First ensure the value is non-null:
val type = value?.let { it::class }
This produces null when value is null.
Should I compare value::class to a class literal to check a type?
Only when you need an exact class match. Prefer value is SomeType for normal type checks because it includes subclasses and enables smart casts.
Do I need a reflection dependency to use ::class?
Basic class literals and runtime KClass references work in Kotlin. Some advanced reflection operations, such as detailed member inspection, require the kotlin-reflect library on JVM projects.
Mini Project
Description
Build a small runtime type reporter for values received from an untyped source, such as decoded JSON, command-line input, or a generic event payload. The reporter demonstrates obtaining a value's KClass, handling null, and using is when behavior should apply to a whole type family.
Goal
Create a function that returns a useful description of any nullable value, including its runtime Kotlin class when present.
Requirements
Create a describeValue function that accepts Any?. Handle null without attempting to read its class. Include the runtime class simple name for non-null values. Add a special description for String values. Test the function with at least four different values.
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.