Skip to content

Commit

Permalink
fix: kt-48004 sealed hierarchy example (JetBrains#2497)
Browse files Browse the repository at this point in the history
* fix: kt-48004 sealed hierarchy example

* chore: clarify indirect inheritance in sealed-classes.md

* Apply suggestions from code review

Co-authored-by: Andrey Polyakov <[email protected]>

* chore: rewrote example in sealed-classes.md

* chore: simplify text

Co-authored-by: Andrey Polyakov <[email protected]>
  • Loading branch information
p7nov and koshachy authored Sep 7, 2021
1 parent dc2a528 commit 29611ad
Showing 1 changed file with 31 additions and 15 deletions.
46 changes: 31 additions & 15 deletions docs/topics/sealed-classes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[//]: # (title: Sealed classes)

_Sealed_ classes and interfaces represent restricted class hierarchies that provide more control over inheritance.
All subclasses of a sealed class are known at compile time. No other subclasses may appear after
All direct subclasses of a sealed class are known at compile time. No other subclasses may appear after
a module with the sealed class is compiled. For example, third-party clients can't extend your sealed class in their code.
Thus, each instance of a sealed class has a type from a limited set that is known when this class is compiled.

Expand All @@ -12,16 +12,23 @@ In some sense, sealed classes are similar to [`enum`](enum-classes.md) classes:
for an enum type is also restricted, but each enum constant exists only as a _single instance_, whereas a subclass
of a sealed class can have _multiple_ instances, each with its own state.

As an example, consider a library's API. It's likely to contain error classes to let the library users handle errors
that it can throw. If the hierarchy of such error classes includes interfaces or abstract classes visible in the public API,
then nothing prevents implementing or extending them in the client code. However, the library doesn't know about errors
declared outside it, so it can't treat them consistently with its own classes. With a sealed hierarchy of error classes,
library authors can be sure that they know all possible error types and no other ones can appear later.

To declare a sealed class or interface, put the `sealed` modifier before its name:

```kotlin
sealed interface Expr
sealed interface Error

sealed class IOError(): Error

sealed class MathExpr(): Expr
class FileReadError(val f: File): IOError()
class DatabaseError(val source: DataSource): IOError()

data class Const(val number: Double) : MathExpr()
data class Sum(val e1: Expr, val e2: Expr) : MathExpr()
object NotANumber : Expr
object RuntimeError : Error
```

A sealed class is [abstract](classes.md#abstract-classes) by itself, it cannot be instantiated directly and can have `abstract` members.
Expand All @@ -30,14 +37,13 @@ Constructors of sealed classes can have one of two [visibilities](visibility-mod
`private`:

```kotlin
sealed class MathExpr {
sealed class IOError {
constructor() { /*...*/ } // protected by default
private constructor(vararg operands: Number): this() { /*...*/ } // private is OK
// public constructor(s: String): this() {} // Error: public and internal are not allowed
private constructor(description: String): this() { /*...*/ } // private is OK
// public constructor(code: Int): this() {} // Error: public and internal are not allowed
}
```


## Location of direct subclasses

Direct subclasses of sealed classes and interfaces must be declared in the same package. They may be top-level or nested
Expand All @@ -50,6 +56,16 @@ Subclasses of sealed classes must have a proper qualified name. They can't be lo
>
{type="note"}

These restrictions don't apply to indirect subclasses. If a direct subclass of a sealed class is not marked as sealed,
it can be extended in any ways that its modifiers allow:

```kotlin
sealed interface Error // has implementations only in same package and module

sealed class IOError(): Error // extended only in same package and module
open class CustomError(): Error // can be extended wherever it's visible
```

### Inheritance in multiplatform projects

There is one more inheritance restriction in [multiplatform projects](mpp-intro.md): direct subclasses of sealed classes must
Expand All @@ -69,11 +85,11 @@ If it's possible to verify that the statement covers all cases, you don't need t
However, this works only if you use `when` as an expression (using the result) and not as a statement:

```kotlin
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
// the `else` clause is not required because we've covered all the cases
fun log(e: Error) = when(e) {
is FileReadError -> { println("Error while reading file ${e.file}") }
is DatabaseError -> { println("Error while reading from database ${e.source}") }
RuntimeError -> { println("Runtime error") }
// the `else` clause is not required because all the cases are covered
}
```

Expand Down

0 comments on commit 29611ad

Please sign in to comment.