하나의 부모 Class Parent
가 존재하고 해당 Class를 상속받는 여러개의 자식 Class Child
가 여러개 존재한다고 가정해보자.
이때 컴파일러는 Parent
를 상속받는 자식 Class Child
가 얼마나 존재하는지 애초에 존재는 하는지 알지 못한다.
예를 들어, 사용자의 상태를 클래스로 나타내기 위해 추상 클래스 PersonState
를 만들고 이를 상속받는 3개의 자식 클래스 Running
, Walking
, Idle
을 만들고자 할 때, 아래와 같이 코드로 구현할 수 있다.
abstract class PersonState
class Running : PersonState()
class Walking : PersonState()
class Idle : PersonState()
이때 각 PersonState
별로 상태 메세지를 얻고자 한다면
fun getStateMessage(personState: PersonState): String {
return when (personState) {
is Running -> "Person is running"
is Walking -> "Person is walking"
is Idle -> "Person is doing nothing"
}
}
위와 같이 getStateMessage
함수를 만들 수 있다.
하지만 이 코드는 else
branch를 추가하라는 오류 메세지를 발생시킨다.
개발자는 PersonState
의 자식 클래스가 Running
, Walking
, Idle
3가지 밖에 없다는 걸 알지만 컴파일러 입장에서는 PersonState
의 자식 클래스가 얼마나 있는지 모르니 else
로 예외 처리를 필요하다고 하는 거다.
하지만 반대로 else
branch를 추가하는 대신 다른 PersonState
상태에 대해 처리해주지 않는다면?
fun getStateMessage(personState: PersonState): String {
return when (personState) {
is Running -> "Person is running"
is Walking -> "Person is walking"
else -> "No State"
}
}
이 코드는 정상적으로 컴파일된다. 하지만 Idle
상태를 가지고 있을때도 "No State"를 출력하는 정상적이지 않은 동작을 할 것이다.
위와 같은 문제를 Sealed Class를 이용해서 효과적으로 해결할 수 있다.
Sealed Class
는 추상 클래스(abstract class)의 하나로써 상속 받는 자식 클래스의 종류를 제한하는 특성을 갖고 있다.
위의 예시 코드를 Sealed Class를 사용해서 구현하면 아래와 같다.
sealed class PersonState
class Running : PersonState()
class Walking : PersonState()
class Idle : PersonState()
fun getStateMessage(personState: PersonState): String {
return when (personState) {
is Running -> "Person is running"
is Walking -> "Person is walking"
is Idle -> "Person is doing nothing"
}
}
Sealed Class로 PersonState 클래스를 구현하면 getStateMessage
함수가 오류를 발생시키지 않는다.
Sealed Class가 자식 클래스를 Running
, Walking
, Idle
3가지로만 제한했다는 것을 컴파일러가 알고 있기 때문이다.
sealed class PersonState
class Running : PersonState()
class Walking : PersonState()
class Idle : PersonState()
이 Sealed Class는 정상적으로 작동하나 한 가지 문제가 있다. class에 주의(밑줄 표시)가 뜨는 것을 확인할 수 있는데 경고문은 다음과 같다.
sealed
subclass has no state and no overriddenequals()
즉, 상태(변수)가 있거나 equals()
를 override할 때만 class로 상속 받아야한다는 뜻이다.
위의 예시와 같은 경우에는 class가 아닌 object
로 상속받을 수 있다.
sealed class PersonState
object Running : PersonState()
object Walking : PersonState()
object Idle : PersonState()
sealed
는 Class 뿐만 아니라 Interface
에서도 비슷하게 활용 가능하다.
sealed interface Error
sealed class IOError(): Error
class FileReadError(val file: File): IOError()
class DatabaseError(val source: DataSource): IOError()
object RuntimeError : Error
레퍼런스)