What does `KClass.sealedSubclasses` give you, and what are its limitations for non-sealed or deeply nested hierarchies?
answer
- sealedSubclasses = direct children of a sealed type
- empty for non-sealed
- one level deep -> recurse for nested sealed
- order not guaranteed
- needs kotlin-reflect
basics
~10 ssealedSubclasses lists the direct subtypes of a sealed class or interface as KClass objects. For a non-sealed class it is empty. It only returns the immediate children, not grandchildren.
solid answer
~40 s`KClass<T>.sealedSubclasses: List<KClass<out T>>` returns the *directly declared* subclasses of a `sealed` class or interface. Because the compiler knows a sealed type's complete set of direct subtypes (they must live in the same module/package scope), reflection can enumerate them — this is the basis for serializers, exhaustive registries, and discriminated unions. Limitations: (1) it returns an empty list for non-sealed types; (2) it is **only one level deep** — if a sealed subtype is itself sealed, you must recurse to reach the leaves; (3) the order is not guaranteed to be stable across compilations; (4) like the rest of the rich API, it requires `kotlin-reflect`. A typical leaf-collection recurses, and combines with `objectInstance` to materialize the no-state variants.
code
kotlin · 8 linessealed interface Json
data class JStr(val v: String) : Json
data class JNum(val v: Double) : Json
object JNull : Json
val registry: Map<String?, KClass<out Json>> =
Json::class.sealedSubclasses.associateBy { it.simpleName }
// {"JStr"=..., "JNum"=..., "JNull"=...}go deeper
Knows it enumerates a sealed type's subtypes as KClasses.
Knows it is direct-only, empty for non-sealed, and order is unspecified.
Recurses nested sealed hierarchies and combines with objectInstance/associateBy for registries.
Weighs reflective enumeration vs compile-time when exhaustiveness and the kotlin-reflect cost in build/runtime.
## The property `sealedSubclasses: List<KClass<out T>>` on `KClass<T>` returns the immediate, directly-declared subclasses of a `sealed class` or `sealed interface`. ### Why it is possible A `sealed` type has a *closed* set of direct subtypes known at compile time (they must be declared in the same module, and historically the same file/package). The compiler records this set as metadata, so reflection can read it back. A normal `open` class has an open-ended subtype set, so `sealedSubclasses` is **empty** for it. ```kotlin sealed interface Shape data class Circle(val r: Double) : Shape data class Square(val side: Double) : Shape object Empty : Shape fun main() { Shape::class.sealedSubclasses.forEach { println(it.simpleName) } // Circle, Square, Empty (order not guaranteed) } ``` ## Limitation 1 — direct children only It does **not** flatten nested sealed hierarchies. You must recurse: ```kotlin fun <T : Any> KClass<T>.allLeafSubclasses(): List<KClass<out T>> = sealedSubclasses.flatMap { sub -> @Suppress("UNCHECKED_CAST") val nested = (sub as KClass<T>).sealedSubclasses if (nested.isEmpty()) listOf(sub) else (sub).allLeafSubclasses() } ``` Use a recursive walk to reach the true leaves of a nested union. ## Limitation 2 — non-sealed types `String::class.sealedSubclasses` is `emptyList()`. There is no error; it just reflects that the type is not sealed. ## Limitation 3 — ordering The returned order is an implementation detail and should not be relied on for stable output; sort by `qualifiedName` if you need determinism. ## Common pairing Collect object variants: ```kotlin val singletons = Shape::class.sealedSubclasses.mapNotNull { it.objectInstance } // [Empty] ``` Or build a name->KClass registry for deserialization: ```kotlin val byName = Shape::class.sealedSubclasses.associateBy { it.simpleName } ``` ## Runtime requirement Like `objectInstance` and `isSubclassOf`, `sealedSubclasses` is part of the full reflection surface and needs `kotlin-reflect` on the classpath. ## Enums note Enums are a different closed set — use the enum's own `entries`/`values()` rather than `sealedSubclasses` for enum constants.
- How do you get the full leaf set of a nested sealed hierarchy?Recurse: for each subclass that is itself sealed, descend into its own `sealedSubclasses`; otherwise treat it as a leaf.
- What does `sealedSubclasses` return for a non-sealed open class?An empty list — there is no closed subtype set to enumerate.
saying these in an interview costs you the question
- Assuming it returns all transitive descendants, not just direct ones
- Relying on a stable ordering of the list
- Expecting it to work (non-empty) on `open` classes
- Using it for enum constants instead of `entries`