skip to content

How do you obtain a KClass in Kotlin, and what is the difference between writing `instance::class` and `String::class`?

level: juniorimportance: must knowfreq 70%

answer

  1. ::class operator returns KClass
  2. Type::class = static declared class
  3. instance::class = runtime class of the value
  4. simpleName / qualifiedName are nullable
  5. generics erased -> use typeOf for KType

basics

~20 s

Use ::class. On a value like x::class you get the real runtime type of that object. On a type name like String::class you get that exact declared class. Both give a KClass, Kotlin's reflection handle for a class.

solid answer

~40 s

Kotlin exposes a class reference through the `::class` operator, producing a `kotlin.reflect.KClass<T>`. There are two forms. `Type::class` (a class literal on a type name, e.g. `String::class`) resolves statically to that exact declared class and has type `KClass<String>`. `instance::class` (a bound class reference on an expression) returns the *runtime* class of the object the expression evaluates to, so for `val a: Any = "hi"; a::class` you get `String::class`, not `Any`. The instance form is non-null and evaluates its receiver. Common members you read off a KClass include `simpleName`, `qualifiedName`, and `isInstance()`. The full member set (e.g. `members`, `objectInstance`) requires the `kotlin-reflect` dependency at runtime, but `simpleName`/`qualifiedName` and basic identity work from the stdlib alone.

code

kotlin · 8 lines
kotlin
open class Animal
class Dog : Animal()

fun main() {
    val a: Animal = Dog()
    println(Animal::class.simpleName) // Animal (static)
    println(a::class.simpleName)      // Dog    (runtime)
}

go deeper

for a junior

Knows ::class yields a KClass and can read simpleName/qualifiedName.

for a middle

Distinguishes Type::class (static) from instance::class (runtime) and explains the out-projection.

for a senior

Connects to erasure (no generics on KClass) and notes which members need kotlin-reflect vs stdlib.

for a principal

Reasons about where to use bound vs literal references in framework/registry code and the cost/erasure trade-offs.

## What a KClass is `KClass<T>` (from `kotlin.reflect`) is Kotlin's reflection representation of a class or interface — the entry point for inspecting a type at runtime (its name, members, supertypes, whether it is an object, etc.). It is the Kotlin analogue of Java's `java.lang.Class`, but richer (it knows about Kotlin concepts like `object` singletons, sealed subclasses, and nullability of members). ## Two ways to get one with `::class` ### 1. Class literal on a type — `Type::class` ```kotlin val k: KClass<String> = String::class // statically resolved ``` Here `String` is a *type name*, so the compiler resolves the class at compile time. The static type is `KClass<String>`. Use this when you know the type literally. ### 2. Bound class reference on an instance — `expr::class` ```kotlin val a: Any = "hi" val k = a::class // KClass<out Any> at compile time, String::class at runtime println(k.simpleName) // String ``` Here the receiver expression is *evaluated*, and you get the **actual runtime class** of the resulting object, regardless of the declared static type. This is polymorphic dispatch for types. The compile-time type is `KClass<out T>` (a projection, because the runtime type may be a subtype). ## Key consequence: static vs runtime - `SomeType::class` ignores any runtime value — it is the declared type. - `value::class` follows the object, so a `List<*>` reference might report `ArrayList`. ## Reading basic info ```kotlin String::class.simpleName // "String" String::class.qualifiedName // "kotlin.String" String::class.isInstance("x") // true ``` `simpleName` and `qualifiedName` are nullable (`String?`) because anonymous and local classes have no such names. ## Generics are erased Because of JVM type erasure, `listOf(1,2)::class` is just `ArrayList::class`; the element type `Int` is gone. To capture full generic type info you use `typeOf<...>()`/`KType` instead — that is a separate mechanism. ## Interop note A `KClass` is stdlib-level for names and identity, but the full reflective surface needs the `kotlin-reflect` artifact on the classpath at runtime.

  • What is the compile-time type of `a::class` when `a: Animal`?
    `KClass<out Animal>` — an out-projection, since the runtime class is some subtype of Animal.
  • Can `simpleName` ever be null?
    Yes, for anonymous objects and certain local classes that have no source name.

Type::class is asking 'what does the label say', instance::class is asking 'what is this thing actually made of'.

saying these in an interview costs you the question

  • Claiming `instance::class` returns the declared/static type rather than the runtime type
  • Saying `::class` gives a `java.lang.Class` directly (that needs `.java`)
  • Believing generic type arguments survive on a KClass
  • Thinking `qualifiedName` is always non-null

context