본문 바로가기
안드로이드/Kotlin

[Kotlin] Enum class, Sealed class

by jinwo_o 2024. 12. 10.

Enum class

  • 상수값의 타입 안전성을 보장하며, when 문에서 상수에 정의된 값에 대한 처리를 반드시 하도록 강제한다.
    • 상수 값에 대한 특정한 동작이 필요할 때 특정 상수값에 대한 동작을 빼먹는 것을 방지할 수 있다.
  • 각 상수는 단 하나의 인스턴스만 가질 수 있다. (싱글톤)
  • 상수의 속성값은 정의 후 변경할 수 없다.
  • 다른 클래스를 상속할 수 없고, 다른 클래스에게 상속받을 수도 없다. 그러나 interface 를 구현할 수 있다.
  • 상수와 연관된 변수를 상수에 저장할 수 있다.
  • 서브 클래스를 생성할 수 없다.
  • private 생성자만 갖는다.

 

BEFORE

  • 문제점 : state 의 종류가 늘어날 경우, 처리 로직을 빼먹거나 처리되지 말아야 할 상수값(Int)이 포함될 수 있습니다. 또한, Int 값이 상수값이므로 state 에 직접 Int 값을 넣어 코딩할 가능성도 존재한다.
class Task {
    var state: Int = DONE

    fun printState() =
        when (state) {
            WAITING -> println("Waiting..")
            PROCESSING -> println("Processing..")
            else -> println("Process not configured")
        }


    companion object {
        const val WAITING: Int = 0
        const val PROCESSING: Int = 1
        const val DONE: Int = 2
    }
}

fun main() {
    val task = Task()
    task.printState() // Process not configured
}

 

AFTER

  • 해결책 : 내부 클래스로 State 라는 enum class 를 생성하고, 그 내부에 두 가지 상수값인 WAITING, PROCESSING 을 정의한다.
class Task {
    var state: State = State.WAITING

    fun printState() =
        when(state){
            State.WAITING -> println("Waiting..")
            State.PROCESSING -> println("Processing..")
            State.DONE -> println("Task done")
        }

    enum class State {
        WAITING, PROCESSING, DONE
    }
}

 

Enum class 활용

1. 공통 함수

BEFORE

  • 문제점 : state 가 추가될 때마다 when 문에 반드시 print를 위한 처리 로직을 추가해야 한다.
class Task {
    var state: State = State.WAITING

    fun printState() =
        when(state){
            State.WAITING -> println("Waiting..")
            State.PROCESSING -> println("Processing..")
            State.DONE -> println("Task done")
        }

    enum class State {
        WAITING, PROCESSING, DONE
    }
}

 

AFTER

class Task {
    var state: State = State.WAITING

    fun printState() = state.print()

    enum class State {
        WAITING, PROCESSING, DONE;

        fun print() = println("${this}..")
    }
}

fun main() {
    val task = Task()
    task.state = Task.State.WAITING
    task.printState() // WAITING..

    task.state = Task.State.PROCESSING
    task.printState() // PROCESSING..

    task.state = Task.State.DONE
    task.printState() // DONE..
}
  • 그러나 공통으로 처리할 때만 사용할 수 있으며, 개별로 처리되어야 할 경우에는 추상 함수를 이용해야 한다.

 

2. 추상 함수

  • 추상 함수가 선언되면, 모든 enum class 의 종류별로 해당 추상 함수를 반드시 구현해야 한다.
class Task {
    var state: State = State.WAITING

    fun printState() = state.print()

    enum class State {
        WAITING {
            override fun print() {
                println("Waiting..")
            }
        },
        PROCESSING {
            override fun print() {
                println("Processing..")
            }
        },
        DONE {
            override fun print() {
                println("Task Done")
            }
        };

        abstract fun print()
    }
}

fun main() {
    val task = Task()
    task.state = Task.State.WAITING
    task.printState() // Waiting..

    task.state = Task.State.PROCESSING
    task.printState() // Processing..

    task.state = Task.State.DONE
    task.printState() // Task Done
}

 

3. enum class 에 변수값 넣기

BEFORE

  • 문제점 : 상수값과 연관된 변수를 상수값 외부에 선언하게 되면 관리가 어려워진다.
class ColorState(R: Int, G: Int, B: Int)

val red = ColorState(255, 0, 0)
val green = ColorState(0, 255, 0)
val blue = ColorState(0, 0, 255)

 

AFTER

  • 클래스 선언부 오른쪽에 상수가 가져야 하는 변수값을 선언한 다음, 상수값에 연관된 변수를 상수 오른쪽에 저장할 수 있다.
enum class Color(R: Int, G: Int, B: Int) {
    RED(255, 0, 0),
    GREEN(0, 255, 0),
    BLUE(0, 0, 255)
}

 

4. enum class 의 모든 변수 가져오기

enum class Color(R: Int, G: Int, B: Int) {
    RED(255, 0, 0),
    GREEN(0, 255, 0),
    BLUE(0, 0, 255)
}

fun main() {
    val colorArray: Array<Color> = Color.values()
    colorArray.forEach {
        println(it)
    }
}

sealed class

  • 추상 클래스로, 자신을 상속받은 여러 서브 클래스들을 가질 수 있다.
  • 상속받는 자식 클래스의 종류를 제한하는 특성을 가지고 있다.
    • 즉, 컴파일러에서 sealed class 의 자식 클래스가 어떤 것이 있는지 알 수 있다.
  • 같은 패키지의 자식 클래스만 상속이 가능하다.
  • sealed class 의 서브 클래스 각각은 여러 개의 인스턴스를 생성할 수 있어, 상태값을 유동적으로 변경할 수 있다.
  • private 생성자만 갖는다.

 

BEFORE

  • 문제점 : 여러 자식 클래스가 하나의 부모 클래스를 상속받았을 때, 컴파일러는 부모 클래스를 상속받은 자식 클래스들이 존재하는지 알지 못한다.
abstract class PersonState

class Running : PersonState()
class Walking : PersonState()
class Idle : PersonState()

 

AFTER

// nested class 로 sealed class 에 포함시키기
sealed class PersonState {
    class Running : PersonState()
    class Walking : PersonState()
    class Idle : PersonState()
}

// nested class 를 이용하지 않고 Sealed class 에 포함시키기
sealed class PersonState
class Running : PersonState()
class Walking : PersonState()
class Idle : PersonState()
  • 경고 : 'sealed' subclass has no state and no overridden 'equals()'
    • 메모리 절약을 위해 상태(변수)가 있거나 equals 를 override 할 경우에만 클래스로 상속받아야 한다.
      • object 로 상속받으면 싱글톤 패턴으로 한 번만 메모리에 올라가고 재사용된다.
  • nested class

 


 

[Kotlin] enum class란 무엇인가?

목표 enum class가 무엇인지 이해한다. enum class가 사용되어야 하는 곳을 이해한다. enum class를 왜 사용해야 하는가? 변수의 상태 별로 다른 결과값을 출력하는 코드를 짜야한다면 어떻게 짜야할까?

kotlinworld.com

 

[Kotlin] enum class의 사용하기

목표 enum class의 사용법을 이해한다. enum class에 대한 함수 만들기 class Task { var state: State = State.WAITING fun printState() = when(state){ State.WAITING -> println("Waiting..") State.PROCESSING -> println("Processing..") State.DONE

kotlinworld.com

 

[Kotlin] Kotlin sealed class란 무엇인가?

sealed class의 등장 배경 여러 자식 Class들이 하나의 부모 Class를 상속 받았다고 했을 때 컴파일러는 부모 Class를 상속 받은 자식 Class들이 있는지 알지 못한다. 예를 들어보자. 우리가 사용자의 런닝

kotlinworld.com

 

 

[Kotlin] Sealed Class 알아보기

Enum Class 의 확장판, Sealed Class 의 개념

velog.io

 

[Kotlin] 클래스 9 — 봉인된 클래스(sealed class), 열거형 클래스(enum class)

Kotlin의 sealed class와 enum class를 정의하고 사용법 그리고 특징에 대해 다룹니다.

medium.com