파게로그
[OOP] Sealed Classes 본문
sealed classes, sealed interfaces는 상속보다 강한 통제를 제공하는, 제한된 클래스 위계를 나타낸다. sealed class의 모든 subclass는 컴파일 타임에 알고 있게 된다. sealed class를 가진 모듈이 컴파일된 이후에는 어떤 subclass도 새로이 등장하지 않는다. 예를 들어, third-party clients는, 그들의 코드 속에 포함되었으나 내가 작성한 sealed class를 상속할 수 없다. 따라서, sealed class의 각 인스턴스는 어떤 제한된 집합 속의 유형 중 하나이며 이러한 유형들은 클래스가 컴파일될 때 모두 알려진다.
sealed classes에 대한 설명은 sealed interfaces와 그 구현체에 대해서도 동일하게 동작한다. sealed interface를 포함한 모듈이 컴파일된 이후에는 어떤 새로운 구현체도 나타날 수 없다.
어떤 의미에서 sealed classes는 enum classes와 유사하다. 왜냐하면, enum type에 대한 값의 집합 또한 제한되어 있기 때문이다. 그러나 각각의 enum 상수는 오직 하나의 인스턴스로서만 존재하지만, sealed class의 subclass는 여러개의 인스턴스를 가질 수 있는데, 각각은 그 상태를 가지고 있는 것이다.
Sealed classes는 어떤 값이 제한된 집합으로부터 하나의 타입만을 가질 수 있을 때에 이용된다(restricted hierarchies).
위 프로그램에서, base class인 Expr은 Const와 Sum이라는 두 개의 derived classes를 가진다. 여기서, when expression에서 default condition으로서 else branch를 사용하는 것은 필수적이다.
이 때, 만약 Expr 클래스로부터 새로운 subclass를 derive한다면, 컴파일러는 아무 문제도 발견하지 못할 것인데, 버그로 이어질 수 있는 이 새로운 subclass는 else branch가 담당하기 때문이다. 그러나 보다 나은 방안은, 새로운 subclass를 추가할 때 컴파일러가 에러를 발생시키는 것이다.
이러한 문제를 해결하고자 sealed class를 사용할 수 있다. 언급했듯이, sealed class는 subclass를 생성할 가능성을 제한한다. 그리고 when expression에서 sealed class의 모든 subclass를 담당하면, else branch는 더이상 필요하지 않다.
sealed class 생성을 위해서 sealed modifier가 사용된다.
sealed interface Expr
sealed class MathExpr(): Expr
data class Const(val number: Double) : MathExpr()
data class Sum(val e1: Expr, val e2: Expr) : MathExpr()
object NotANumber : Expr
sealed class는 그 자체로 추상 클래스이며, 따라서 직접 초기화될 수 없고, abstract 멤버를 가질 수 있다.
sealed classes의 생성자는 다음 두 개 중 하나의 visibility만 가질 수 있다: protected(by default) 또는 private.
sealed class MathExpr {
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
}
Location of direct subclasses
sealed classes의 direct subclasses와 interfaces는 동일한 패키지 내에 선언되어야 한다. 이들은 top-level일 수도 있으며, 또는 몇 개인지와 관계없이 다른 명명된 클래스, 인터페이스, 객체 등의 내부에 nested될 수 있다. subclasses는 보통의 상속 규칙을 따르는 한 어떤 visibility도 가질 수 있다.
sealed classes의 subclasses는 적절한, 올바른 이름을 가져야 한다. 이들은 익명일 수 없고 local할 수도 없다.
Sealed classes and when expression
sealed classes를 사용하는 주요한 이점은 when expression 내에서 sealed classes를 사용할 때 드러난다. 만약 statement가 모든 케이스를 다룬다고 검증하는 것이 가능하다면 statement에 else절을 추가할 필요가 없다. 그러나 이는 when을 statement가 아닌 expression으로 사용할 때에만(결과를 사용할 때에만) 적용된다.
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
}
'콤퓨타 왕기초 > Kotlin' 카테고리의 다른 글
[OOP] Extension Function (0) | 2021.06.13 |
---|---|
[OOP] Object, singleton pattern (0) | 2021.06.13 |
[OOP] Data Class (0) | 2021.06.13 |
[OOP] Nested class and Inner class (0) | 2021.06.13 |
[OOP] Abstract class, Interface (0) | 2021.06.12 |