skip to content

You only need to check an object's type and read its simple name. How do you do that without pulling in kotlin-reflect, and what exactly are the limits of ::class on stdlib alone?

level: middleimportance: should knowfreq 40%

answer

  1. ::class + simpleName/qualifiedName = free
  2. isInstance and is/when don't need kotlin-reflect
  3. .java bridges to java.lang.reflect
  4. Members/constructors/supertypes = need the JAR
  5. Identity is free; structure costs the artifact

basics

~10 s

Use ::class with simpleName/qualifiedName, isInstance, or the is operator — these work without kotlin-reflect. You only hit the wall when you want members, constructors, or supertypes.

solid answer

~40 s

The `::class` operator and the `KClass` it returns are part of stdlib, so you can do lightweight type work with no extra dependency: `obj::class` / `Type::class`, `simpleName`, `qualifiedName`, `isInstance(x)`, equality/identity comparison of KClass instances, and `.java` to drop into plain `java.lang.reflect`. Idiomatic `is`/`as` checks and `when (x) { is Foo -> ... }` don't use reflection at all — they compile to `instanceof`/`checkcast`. The wall appears the moment you touch the structural API in `kotlin.reflect.full`: `memberProperties`, `declaredFunctions`, `constructors`/`primaryConstructor`, `createInstance()`, `supertypes`, `visibility`, `isData`/`isSealed`, or reflective `.call`/`.get`. Those require kotlin-reflect and throw `KotlinReflectionNotSupportedError` if it's absent. Rule of thumb: identity and naming are free; structure and invocation cost the JAR.

code

kotlin · 9 lines
kotlin
// Stdlib-only type handling — no kotlin-reflect required
fun handlerFor(x: Any): String = when (x) {
    is Int    -> "int:${x}"
    is String -> "str:${x.length}"
    else      -> x::class.qualifiedName ?: "<anon>"
}

val cls = listOf(1).first()::class   // KClass<Int>, free
println(cls.simpleName)               // "Int"

go deeper

for a junior

Uses is/when for type checks but is unsure which APIs need the library.

for a middle

Draws the line: naming/identity/isInstance free, structure/invocation needs kotlin-reflect.

for a senior

Knows the .java bridge and what Kotlin metadata is lost when dropping to Java reflection.

for a principal

Designs APIs to stay on the cheap stdlib path and avoids the artifact in size-critical modules by policy.

## What's free on stdlib (no kotlin-reflect) The `::class` literal returns a `KClass<T>`. A useful slice of it works with stdlib only: - **Get the KClass**: `value::class` (runtime class of the instance) or `Foo::class` (the literal type). - **Names**: `simpleName`, `qualifiedName`. - **Type tests**: `kclass.isInstance(obj)`. - **Identity**: comparing two `KClass` values with `==`. - **Java bridge**: `kclass.java` gives a `java.lang.Class`, from which you can use ordinary Java reflection (`java.lang.reflect`) — that is a *different* library and doesn't need kotlin-reflect. Separately, the language operators don't touch reflection at all: ```kotlin when (shape) { is Circle -> shape.radius // compiles to instanceof + checkcast is Square -> shape.side } val ok = obj is User // no reflection, no kotlin-reflect ``` ## A concrete stdlib-only example ```kotlin fun describe(x: Any): String { val k = x::class return "${k.simpleName} (java=${k.java.name}) isString=${String::class.isInstance(x)}" } // describe("hi") -> "String (java=java.lang.String) isString=true" ``` None of the above requires kotlin-reflect. ## Where the wall is (needs kotlin-reflect) Everything *structural* or *invocational* lives behind kotlin-reflect, mostly under `kotlin.reflect.full`: - Members: `memberProperties`, `declaredMemberProperties`, `memberFunctions`, `declaredFunctions`. - Construction: `constructors`, `primaryConstructor`, `createInstance()`. - Hierarchy & modifiers: `supertypes`, `allSuperclasses`, `visibility`, `isData`, `isSealed`, `isAbstract`. - Reflective invocation: `KFunction.call(...)`, `KProperty.getter.call(...)`, `KMutableProperty.setter.call(...)`. Touch any of these without the artifact and the runtime throws `KotlinReflectionNotSupportedError`. ## Decision rule - Need **type identity, naming, or instance checks**? → `::class` / `is` / `isInstance` — **no dependency**. - Need to **enumerate or invoke members, read structure, or construct via reflection**? → add **kotlin-reflect**. - Need Java-level structure but want to avoid kotlin-reflect? → bridge through `.java` and use `java.lang.reflect` (note: it won't see Kotlin-specific concepts like default args, nullability, or properties as Kotlin sees them).

  • Does value::class need kotlin-reflect at runtime?
    No. Getting the KClass and reading simpleName/qualifiedName/isInstance is part of stdlib; only the kotlin.reflect.full structural API needs the artifact.
  • If you bridge via .java to java.lang.reflect, what Kotlin info do you lose?
    Kotlin-specific concepts: nullability, default parameter values, properties (vs getters/setters), inline/suspend semantics, and visibility nuances — Java reflection sees only the JVM-level view.

saying these in an interview costs you the question

  • Thinking 'is'/'when (is)' checks pull in kotlin-reflect
  • Believing memberProperties works on stdlib
  • Not knowing .java bridges to plain Java reflection
  • Adding kotlin-reflect just to read simpleName
  • Assuming java.lang.reflect sees Kotlin nullability/defaults

context