Where are subtypes of a sealed class or interface allowed to be declared? Explain the same-module / same-package permitted-subtype rule.
answer
- Same module + same package (Kotlin 1.5+)
- No longer same-file (that was pre-1.5)
- Module boundary blocks external libraries
- Rule is for DIRECT subtypes only
- Indirect subtypes can live anywhere
basics
~20 sDirect subtypes must be in the same module as the sealed declaration, and in the same package. You can nest them inside the sealed type or put them in separate top-level files, as long as they stay in that module and package.
solid answer
~40 sSince Kotlin 1.5, direct subtypes of a sealed class or interface must satisfy two location constraints: they must be in the **same compilation module** and in the **same package** as the sealed declaration. They no longer have to be nested inside it or even in the same file — separate top-level files in that package are fine. This is the rule that makes the subtype set 'closed': because nothing in another module or package can extend the type, the compiler can enumerate every direct subtype. Before 1.5 the rule was stricter (subtypes had to be in the same file). The module boundary is what blocks third-party libraries from adding cases. Indirect (transitive) subtypes can live anywhere as long as their direct parent already complies.
code
kotlin · 8 lines// package com.app.net, module :core
sealed interface Response
// same package, same module, different file — OK
data class Success(val body: String) : Response
data class Failure(val code: Int) : Response
// A different Gradle module declaring `class Hack : Response` would NOT compile.go deeper
Knows subtypes live near the sealed type but may only recall the file/package intuition.
States both constraints precisely (same module AND same package) and knows the same-file rule was relaxed in 1.5.
Explains why the module boundary is what enforces the closed set and distinguishes direct vs indirect subtypes.
Reasons about module/package layout for evolvable sealed APIs and the compatibility implications of where subtypes are placed.
## The rule (Kotlin 1.5+) A **direct subtype** of a sealed class or sealed interface must be: 1. In the **same module** (the same compilation unit — e.g. the same Gradle source set / module), AND 2. In the **same package** as the sealed declaration. That's it. The subtype does **not** need to be nested inside the sealed type, and does **not** need to be in the same file. ```kotlin // file: events/Event.kt (package com.app.events) package com.app.events sealed interface Event // file: events/UserEvents.kt (SAME package, SAME module — allowed) package com.app.events data class LoggedIn(val userId: String) : Event data class LoggedOut(val userId: String) : Event ``` ## Why these two constraints - **Same package** keeps the closed set discoverable and prevents accidental scattering across namespaces. - **Same module** is the real lock: a separately compiled library or another Gradle module **cannot** add a subtype, even if it could write the same package name. This guarantees the compiler sees the *whole* hierarchy when it compiles the sealed declaration — the foundation of the closed-set property. ## Direct vs indirect subtypes The rule applies to **direct** subtypes (those that list the sealed type directly in their supertype list). An **indirect** subtype — a subtype of one of those direct subtypes — can live in another module or package, because the closed set the compiler cares about is the set of *direct* children. ```kotlin sealed interface Result // module A, package p class Ok : Result // module A, package p (direct — must comply) // in module B: class SpecialOk : Ok() // indirect — allowed elsewhere ``` ## History - **Before 1.5:** all subtypes had to be in the **same file** (often nested). - **1.5 onward:** relaxed to same module + same package, and `sealed interface` was introduced. ## Common placement patterns - Nesting subtypes inside the sealed type (groups them, namespaces them). - Top-level declarations in the same package file(s) — better for large hierarchies.
- Do all subtypes have to be in the same file?Not since Kotlin 1.5. Same module and same package is enough; before 1.5 they had to be in the same file.
- Can a subtype's own subtype (an indirect subtype) live in another module?Yes. The same-module/same-package rule constrains only direct subtypes; indirect ones can be declared elsewhere.
saying these in an interview costs you the question
- Insisting subtypes must be in the same file (outdated pre-1.5 rule)
- Saying any package in the same module is fine (package must match too)
- Claiming another module can add subtypes if it reuses the package name
- Not distinguishing direct from indirect subtypes