How do `isSubclassOf()` / `isSuperclassOf()` work on KClass, and how do they differ from `isInstance()` and the `is` operator?
answer
- isSubclassOf compares class to class, reflexive + transitive
- isSuperclassOf is the inverse
- in kotlin.reflect.full, needs kotlin-reflect
- is operator / isInstance test a value, not a class
- all erase generics
basics
~20 sa.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 linesimport 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) -> truego deeper
Recognizes that is tests a value's type; may not know the reflective forms.
Knows isInstance is the reflective version of is and uses it with KClass tokens.
Distinguishes isSubclassOf (class-vs-class, reflexive/transitive, in kotlin.reflect.full) from is/isInstance and notes the smart-cast and erasure differences.
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