Sealed class vs Sealed interface vs Enum

DoDoBest

·

2024. 3. 5. 20:21

Sealed class

sealed class는 같은 모듈의 같은 패키지에서만 상속할 수 있는 클래스로, 유한한 개수의 subclass로 구성된 closed type hierarchy를 제공하는 클래스다. 또한 상속은 런타임이 아닌 컴파일 타임에만 허용된다. 그래서 third-party 코드에서 sealed class를 상속하는 클래스를 정의할 수 없다. abstract class와 대비되는 것을 알 수 있다.

 

아래와 같이 MyResult가 정의되어 있는 패키지와 다른 a 패키지에 정의되어 있는 MyResponse 클래스가 MyResult Sealed class를 상속하지 못하는 것을 볼 수 있다. 

Inheritor of sealed class or interface declared in package a but it must be in package <root> where base class is declared

 

같은 패키지라면 꼭 MyResult와 같은 파일이 아니더라도 상속하는 클래스를 정의할 수 있다.

 

 

 

Sealed class를 상속하는 클래스가 무엇인지 분명하기 때문에, when 절에서 else 없이 사용할 수 있다는 장점이 있다. 예를 들어 MyResult 클래스를 상속하는 클래스가 두 개 뿐이라고 하면 다음과 같이 사용할 수 있다.

sealed class MyResult {
    class Success(val result: String): MyResult()
    class Fail(val responseCode: Int): MyResult()
}

val response: MyResult = getResponseFromSomething()

when (response) {
    is MyResult.Fail -> {}
    is MyResult.Success -> {}
}

 

 

Sealed interface

Sealed class와 상속 가능한 특징은 동일하나, class와 interface의 차이에 기인한 다음과 같은 차이가 있다.

  • class는 생성자를 정의할 수 있으나, interface는 생성자 정의가 불가능하다.
  • class는 abstract mothod와 property를 가질 수 있으나, interface는 abstract method만 정의할 수 있다.(abstract method에 대한 default 구현 제공 가능)

+2024.04.18

추가로 다음과 같은 차이가 있다.

https://youtu.be/agjbbn9Swkc?si=N2Kfw6zr3sqX17Il&t=390

 

그러면 Sealed interface를 언제 사용하는가?

  1. Interface는 class에 비해 메모리를 적게 차지하기 때문에, interface로도 충분하면 inteface로 선언해 메모리 이점을 얻을 수 있다.
  2. interface는 class를 상속할 수 없다. sealed 하위에 interface를 선언해야 한다면, sealed class는 불가능하므로 sealed interface를 사용해야 한다.
  3. class는 1개의 클래스만 상속할 수 있다. 만약 하위 클래스가 sealed가 아닌 다른 클래스를 상속해야 한다면, sealed를 interface로 선언하여 하위 클래스가 다른 클래스를 상속하도록 할 수 있다.

 

Sealed class와 Enum class는 무엇이 다른가?

다음 코드를 보면 동일한 기능을 제공하는 것을 볼 수 있다.

sealed class HttpError(val code: Int) {
    object Unauthorized: HttpError(401)
    object NotFound: HttpError(404)

    fun doSomeThing() {}
}

enum class HttpErrorEnum(val code: Int) {
    Unauthorized(401), NotFound(404);

    fun doSomeThing() {}
}

fun main() {
    val error: HttpError = HttpError.NotFound
    error.code // Enum과 같이 생성자 파라미터를 정의한 후 사용할 수 있다.
    when (error) {
        HttpError.NotFound -> Unit
        HttpError.Unauthorized -> Unit
    }

    val errorEnum: HttpErrorEnum = HttpErrorEnum.NotFound
    when (errorEnum) {
        HttpErrorEnum.NotFound -> TODO()
        HttpErrorEnum.Unauthorized -> TODO()
    }
}

 

그러면 차이는 무엇일까? Enum class body에 있는 요소들은 상수(Constant)다. 그래서 enum constants, enum entries 라고 부른다. enum constant는 고유한 variable, parameter 등을 가질 수 없다. Unauthorized는 모두 code라는 변수만 가지고 있으며, 401이라는 동일한 값으로 설정된다. 위 HttpError는 object로 선언되어 code 외에 변수가 없지만, 아래와 같이 object 키워드를 지워서 파라미터를 가지도록 설정할 수 있다.

sealed class HttpError(val code: Int) {
    data class Unauthorized(val reason: String): HttpError(401)
    ...
}

 

 

정리하면, sealed class는 enum에 비해 더 많은 customization을 제공한다.

하위 클래스가 개별적인 행동(behavior)이나 함수, 변수를 제공할 필요가 없다면 enum class를 사용하면 된다.

 

sealed class와 다른 enum에서만 제공하는 기능

enum class는 가능한 모든 값을 values 또는 entries를 이용해 얻을 수 있다.

HttpErrorEnum.values().forEach(::println)
HttpErrorEnum.entries.forEach(::println)

 

sealed class는 하위 클래스 객체를 직접 생성해서 리스트로 만들어 loop 해야 한다.

 

 

 

참고 자료

https://kotlinlang.org/docs/sealed-classes.html

 

Sealed classes and interfaces | Kotlin

 

kotlinlang.org

https://medium.com/@manuchekhrdev/sealed-class-vs-sealed-interface-in-kotlin-47222335040a

 

Sealed Class vs Sealed Interface in Kotlin

Sealed class and Sealed interface are two features in Kotlin that allow the creation of restricted class hierarchies. Both of these…

medium.com

https://www.youtube.com/watch?v=kLJRZpRhX1o

 

'학습 > CS' 카테고리의 다른 글

If I can use if, when to use when  (0) 2024.03.06
Kotlin에서 data class는 왜 사용할까  (0) 2024.02.16