skip to content

How do `isSubclassOf()` / `isSuperclassOf()` work on KClass, and how do they differ from `isInstance()` and the `is` operator?

level: seniorimportance: should knowfreq 35%

answer

  1. isSubclassOf compares class to class, reflexive + transitive
  2. isSuperclassOf is the inverse
  3. in kotlin.reflect.full, needs kotlin-reflect
  4. is operator / isInstance test a value, not a class
  5. all erase generics

basics

~20 s

a.isSubclassOf(b) checks whether class a is the same as or descends from class b — a class-to-class relationship. isInstance(x) and is check whether a value belongs to a type. The first compares two KClasses; the others test an object.

solid answer

~40 s

`isSubclassOf(base: KClass<*>)` and its inverse `isSuperclassOf(derived)` are extension functions in `kotlin.reflect.full` that compare two *classes*. `A::class.isSubclassOf(B::class)` is true when A equals B or A transitively extends/implements B; it is reflexive (a class is a subclass of itself). They walk the supertype graph and require `kotlin-reflect`. In contrast, `KClass.isInstance(obj)` and the `is` operator test whether a *value* is of a type — `is` is a compile-time-checked, erasure-aware operator that needs no reflection, while `isInstance` is the reflective, dynamic equivalent useful when the type is only known as a `KClass` at runtime. So: class-vs-class -> `isSubclassOf`; value-vs-type known statically -> `is`; value-vs-type held as a KClass -> `isInstance`. None of them recover erased generic arguments.

code

kotlin · 9 lines
kotlin
import kotlin.reflect.full.isSubclassOf

fun describe(k: KClass<*>, value: Any) {
    println(k.isSubclassOf(Number::class)) // class relationship
    println(k.isInstance(value))           // value-of-type (reflective)
}

// Number::class.isInstance(3)  -> true
// Int::class.isSubclassOf(Number::class) -> true

go deeper

for a junior

Recognizes that is tests a value's type; may not know the reflective forms.

for a middle

Knows isInstance is the reflective version of is and uses it with KClass tokens.

for a senior

Distinguishes isSubclassOf (class-vs-class, reflexive/transitive, in kotlin.reflect.full) from is/isInstance and notes the smart-cast and erasure differences.

for a principal

Designs plugin/registry dispatch choosing reflective subclass checks vs compile-time polymorphism, mindful of cost and erasure.

## Two different questions 1. **Is one class related to another class?** -> `isSubclassOf` / `isSuperclassOf`. 2. **Is this object of a given type?** -> `is` operator or `KClass.isInstance(value)`. ## isSubclassOf / isSuperclassOf These live in package `kotlin.reflect.full` (import them) and are part of the full reflection API (need `kotlin-reflect`). ```kotlin import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSuperclassOf open class Animal open class Dog : Animal() class Puppy : Dog() Puppy::class.isSubclassOf(Animal::class) // true (transitive) Animal::class.isSubclassOf(Animal::class) // true (reflexive!) Animal::class.isSubclassOf(Dog::class) // false Animal::class.isSuperclassOf(Puppy::class) // true ``` Key properties: - **Reflexive**: a class is a subclass of itself. - **Transitive**: walks the whole supertype chain, including interfaces. - Works purely on KClass metadata — no instance involved. ## isInstance vs the `is` operator ```kotlin val k: KClass<*> = CharSequence::class k.isInstance("hello") // true, decided at runtime from the KClass val v: Any = "hello" v is CharSequence // true, but the type is written literally in source ``` - `is` is a language operator. The compiler emits an erasure-aware `instanceof`-style check and can perform smart-casts afterward. You write the type literally; you cannot pass a `KClass` to it. - `isInstance(obj)` is a reflective method on a `KClass` you may have computed at runtime (e.g. from `sealedSubclasses`). It returns a plain `Boolean` with no smart-cast. ## Relationship between them `base.isInstance(x)` is roughly `x != null && x::class.isSubclassOf(base)` — the instance check can be expressed via the runtime class subclass check. ## Generics caveat All of these operate on erased classes. `List::class.isSubclassOf(Collection::class)` is fine, but you cannot ask 'is this a `List<String>`' through any of them — the element type is erased. Use `KType`/`typeOf` for parameterized checks. ## When to use which - Comparing two type tokens you hold as KClass -> `isSubclassOf`. - Filtering a value when the target type is statically known and you want smart-cast -> `is`. - Filtering a value against a dynamically-obtained type token -> `isInstance`.

  • Is `A::class.isSubclassOf(A::class)` true?
    Yes — the relation is reflexive; a class is considered a subclass of itself.
  • Why can't `isInstance` smart-cast like `is`?
    `isInstance` returns a runtime Boolean from a KClass the compiler cannot tie to a static type, so it cannot narrow the variable's type.
  • Where are `isSubclassOf`/`isSuperclassOf` declared?
    As extension functions in `kotlin.reflect.full`, part of the kotlin-reflect library.

saying these in an interview costs you the question

  • Saying `isSubclassOf` is not reflexive
  • Confusing class-vs-class (`isSubclassOf`) with value-vs-type (`is`/`isInstance`)
  • Claiming `is` requires kotlin-reflect (it does not)
  • Expecting any of them to respect generic type arguments

context