실드 클래스(와 인터페이스는)는 보통의 상속 구조를 넘어선 더 제한된 상속제어하는 기능을 제공한다.
실드 클래스의 직속 서브클래스들은 컴파일 시에 컴파일러가 어떤 하위 클래스들이 있는지 알고있다.
그래서 클래스를 인스턴스화 했을 때, 그 인스턴스는 제한된 타입만을 가지게 된다.
또한 실드 클래스가 정의된 모듈(패키지) 밖에서 서브클래스를 정의할 수 없음.
실드 인터페이스도 똑같이 동작함
실드 클래스의 각 인스턴스는 이 클래스가 컴파일 됐을 때 알려진 제한된 정보들만 가지고있을 수 있음. 서드파티의 클라이언트가 함부로 확장해서 못씀.
enum 사용하면 안되나? 생각 할 수 있는데 enum은 한가지 인스턴스로만 존재를 하고,
실드 클래스의 서브클래스는 각각의 상태에 따라 여러개의 인스턴스들을 가진 수 있음.(각각 가지고있는 데이터나 메소드 같은걸 다 구현해 줄 수 있음.)
[예시] API 라이브러리
에러클래스가 인터페이스나 abstract 클래스를 구조를 가지고있으면 사용하는 쪽에서 확장하는걸 못 막고, 바깥쪽에서 정의된 에러에 대한거는 몰라 그래서 바깥쪽에서 만들어진것을 그 클래서에서 지속적으로 관리를 못해. 실드 구조를 가지고있으면 라이브러리 만든 사람이 모든 가능한 에러타입을 보장할 수 있고 다른 알수없는 에러가 나올 수가 없다.
사용 방법
sealed interface Error
sealed class IOError(): Error
class FileReadError(val file: File): IOError()
class DatabaseError(val source: DataSource): IOError()
object RuntimeError : Error
sealed class는 자체적으로 abstract다.
직접적으로 인스턴스화 할 수 없고 abstract 멤버를 가진다
constructor 기본이 protected이고 private으로 선언하는것도 가능하다.(public이나 internal은 안됨)
sealed class IOError {
constructor() { /*...*/ } // ok
private constructor(description: String): this() { /*...*/ } // ok
public constructor(code: Int): this() {} // error
}
direct subclass는 같은 패키지 안에서 선언되어야만 함.
서브 클래스는 탑레벨이거나 명명된(이름이 있는) object, 인터페이스, 클래스의 내부 중첩 클래스 일 수도 있음.
그럴 경우 코틀린의 보통 상속 규칙에서 가능한 visibility를 가질 수 있음.
실드 클래스의 서브 클래스는 적절한 정규화된 이름을 가져야만 함.(local이나 익명 오브젝트는 안됨.)
enum은 다른 클래스와 마찬가지로 실드 클래스로 확장이 불가능하다. 그러나 sealed interface를 구현하는건 가능하다.
이런 제한들은 간접 서브 클래스에서는 적용되지 않음. 실드 클래스의 직속 서브클래스가 실드로 표시되어있지 않다면 modifiers에 따라서 확장이 가능함.
sealed interface Error // 같은 패키지나 모듈 내에서만 구현 가능
sealed class IOError(): Error // 같은 패키지나 모듈 내에서는 확장에서만 가능
open class CustomError(): Error // 가시성을 어디에서든 확장 가능
common 소스에서 실드 클래스가 expect로 정의되고, platform 소스에서 actual로 구현되어있다면, 소스에서 서브클래스를 만들 수 있음. 게다가 상속 구조를 사용하고 있다면 expect와 actual 정의 사이의 어떤 소스에서도 서브 클래스를 만들 수 있음.
실드 클래스의 가장 좋은 장점은 when을 사용할 수 있음.
컴파일러가 서브 클래스를 모두 알고있기때문에 이것들만 다 다루고있으면 else를 안써도 .
멀티플랫폼 프로젝트의 common 코드에 expect
실드 클래스의 when
은 else
를 필요로함.
actual 플랫폼에서 어떻게 서브 클래스를 정의 했는지를 컴파일러는 모르니까