Apple is Apple
Published 2023. 9. 15. 16:44
Sealed Class Language/Kotlin

Sealed Class (& enum class)

등장 배경

여러 자식 클래스들이 하나의 부모 클래스를 상속받았다고 할 때, 컴파일러는 어떤 클래스들이 부모 클래스를 상속받았는지 알지 못한다. 그래서 이 문제를 해결하기 위해 sealed class라는 개념이 나왔는데 sealed class는 자신은 추상클래스이고, 자신을 상속받는 자식 클래스의 종류를 제한하는 특성을 가지고 있다. 컴파일 시 sealed class의 자식이 어떤 것이 있는지 알 수 있다는 것이다.

 

자식 클래스의 종류를 제한하기 때문에 다음과 같은 특성이 있다.

  • 실드란 '봉인된'이라는 의미로 무언가 안전하게 보관하기 위해 묶어 두는 것
  • 실드 클래스 그 자체로는 추상 클래스와 같기 때문에 객체를 만들 수는 없다.
  • 생성자도 기본적으로는 private이며 private이 아닌 생성자는 허용하지 않음
  • 실드 클래스는 같은 파일 안에서는 상속이 가능 (다른 파일에서 상속 불가) 블록 안에 선언되는 클래스는 상속이 필요한 경우 open 키워드로 선언

이와 같은 특성 때문에 sealed class를 상속 받은 클래스는 한 곳에 모아 관리하기 쉽다는 장점이 있다.

(일반 클래스를 무분별하게 상속하다보면 어디서 뭐가 쓰인 지 헷갈릴 수도 있을 것이다.)

 

또,  when절에서 발생할 수 있는 문제를 해결할 수 있는데....

 

예를 들어 보자

abstract class State

class Success: State()
class Error: State()
class Loading: State()

State라는 부모 클래스를 상속 받는 3개의 클래스가 있다고 하고, 각 클래스 별로 상태를 얻어보고자 하자

fun getState(state: State) {
	when(state) {
    	is Success -> {}
        is Error -> {}
        is Loading -> {}
    }
}

위와 같이 할 수 있을 것이다. 하지만 이렇게 코드를 작성하면 IDE 상에서 else branch를 작성하라는 에러를 나타낼 것이다. 왜냐하면 State를 상속받는 클래스의 종류를 알지 못하기 때문에 예외에 대해 처리해 줘야 된다는 것이다.

fun getState(state: State) {
	when(state) {
    	is Success -> {}
        is Error -> {}
        is Loading -> {}
        else -> {}
    }
}

여기서 sealed class를 해결하면 문제를 쉽게 해결할 수 있다.

sealed class State

class Success: State()
class Error: State()
class Loading: State()
fun getState(state: State) {
	when(state) {
    	is Success -> {}
        is Error -> {}
        is Loading -> {}
    }
}

위에서 설명한 sealed class의 특성인 자식 클래스를 제한하는 점에서 강점을 나타낸다. sealed class를 상속받는 자식클래스들을 이미 알고 있기 때문에 else branch를 넣지 않아도 아무 문제가 생기지 않는다.

 

이러한 점으로 상태관리를 해야할 때 sealed class가 유용하게 쓰인다.

 

서버 API를 통해 데이터를 받아온다고 해보자

sealed class Response<T>(
    val data: T? = null,
    val message: String? = null
) {
    class Success<T>(data: T? = null): Response<T>(data)
    class Loading<T>(data: T? = null): Response<T>(data)
    class Error<T>(message: String, data: T? = null): Response<T>(data, message)
}

private fun fetchItems() = with(binding) {
    val datas: Response<T> = getDatas()
    when (datas) {
        is Response.Error -> {
           Log.e("error", it.message)
        }

        is Response.Loading -> {
            Log.e("loading", it.data)
        }

        is Response.Success -> {
            it.data?.let { data ->
           
           }
        }    
    }
}

서버에서 데이터를 받아와 상태를 나타내는 Response라는 sealed class를 만들고, 그 안에서 Success, Error, Loading이라는 각각의 상태를 나타내는 자식 클래스 만든다. 자식 클래스에서는 제네릭을 통해 data를 받을 수 있게 한다.

 

datas라는 변수에 데이터를 가져오고 when절을 통해 분기를 시킨다. 가져온 데이터의 상태에 따라 바로 분기시키고 그 안에서 데이터를 사용할 수 있기 때문에 좀 더 구조적으로 보기 좋은 코드를 작성할 수 있다.

 

이와 같이 특정하게 정해진 상태나 구분해야 할 것을 명확하게 해야 할 때는, sealed class를 사용하여 예외 사항 없이 관리할 수 있을 것 같다.

 

 

Enum Class

 

여러 개의 상수를 선언하고 열거된 값을 조건에 따라 선택할 수 있는 특수한 클래스 자료형이 동일한 상수를 나열할 수 있다.  단, 실드 클래스처럼 다양한 자료형을 다루지 못한다

 

enum class Color {
	RED, BLUE, BLACK
}

var color: Color = Color.RED

when(color) {
	Color.RED -> { // 실행 }
    Color.BLUE -> {}
    Color.BLACK -> {}
}

enum class로 sealed class와 비슷하게 사용할 수 있다.

 

sealed class가 enum class보다 다양한 형태를 지원하여 확장된 enum이라고도 한다.

상황에 맞게 sealed class - enum class를 선택하여 사용하면 될 것 같다. 

 

 

ref.

 

Sealed classes and interfaces | Kotlin

 

kotlinlang.org

 

Sealed Class 총정리 feat. enum Class

오늘은 Kotlin의 Sealed Class에 대해서 알아보도록 하겠습니다. 1. Sealed Class 와 Interface 1-1. Sealed Class 와 Interface Seal의 뜻은 밀봉하다 봉인하다는 뜻인데요. SealedClass는 abstract클래스여서 객체로 만들

developer88.tistory.com

 

Enum class와 Sealed class

Enum 클래스와 Sealed 클래스 Enum class vs Sealed clas 👉🏻둘다 타입을 제한적으로 사용하고자 할 때 많이 사용하게 됨. Enum에서는 특정 값을 single instance로서 하나의 객체만 제한적으로 사용할 수 있

velog.io

 

'Language > Kotlin' 카테고리의 다른 글

더블 콜론 참조(::)  (0) 2024.05.09
URL Encoding  (0) 2024.04.02
[Kotlin] Coroutine  (2) 2024.03.07
[Kotlin] Scope function  (0) 2023.07.31
profile

Apple is Apple

@mjjjjjj