Goals:
- Make sealed class hierarchies possible (this implies more exhaustiveness criteria for
when
-expressions)
This option is chosen as final.
Special sealed
keyword is introduced to mark base classes for sealed hierarchies, like this:
sealed class Type () {
class Named(val name: String) : Type()
class Nested {
class Function(val param: Type, val result: Type): Type()
}
object Top: Type()
}
// ...
when (type) {
is Named -> println(name)
is Nested.Function -> println("$param -> $result")
// Alternatively, we can omit is here
is Top -> println("TOP")
}
It's assumed here that sealed class can be subclassed only by nested classes or objects, both with any level of nesting,
but not by local classes of any sort. So the when
example would operate correctly.
Sealed classes are abstract by default, so abstract modifier is redundant
(otherwise when
requires additional case covering all other cases: is Type).
Sealed classes can never be open or final.
Sealed interfaces are prohibited, otherwise Java classes could easily inherit them.
Sealed objects are also not possible, because we cannot inherit from object.
Another option would be to make when
understand hierarchies like this one:
abstract class Type private () {
class Named(val name: String) : Type()
class Function(val param: Type, val result: Type): Type()
object Top: Type()
}
When the superclass has a private constructor, all its subclasses are known to be (directly or indirectly) nested into in,
and we can be sure that a set of is
-checks is exhaustive if all the subclasses are included:
when (type) {
is Named -> println(name)
is Function -> println("$param -> $result")
is Top -> println("TOP")
}
However, class with a private constructor can also be derived as a local class, that provides a problem for this option implementation.
It's possible to optimize when on sealed in the way like when on enum or when on string.
There are the following opportunities, all of them use KSealed
interface with some final
identification method:
- use
ordinal()
method, which is implemented like enums, so the first descendant has an ordinal 0, second 1 and so on. When on sealed organized like when on enum. Drawback: reordering breaks client's code. - use
sealedName()
method returning a fully qualified class name of a direct sealed descendant. When on sealed organized like when on string. Drawback: extra efforts. - use
sealedId()
method returning a hash code of a direct sealed descendant fully qualified class name. Drawbacks: possible collisions, including an opportunity to rename some member and get hash code of another member, which breaks client's code in a dramatic way.
After optimization, instanceof KSealed
should be checked at run-time before applying it.