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 로 상속받으면 싱글톤 패턴으로 한 번만 메모리에 올라가고 재사용된다.
- 메모리 절약을 위해 상태(변수)가 있거나 equals 를 override 할 경우에만 클래스로 상속받아야 한다.
- nested class
'안드로이드 > Kotlin' 카테고리의 다른 글
[Kotlin] 객체 선언(Object Declaration) , 동반 객체(Companion Object) (0) | 2024.12.09 |
---|---|
[Kotlin] 범위 지정 함수(Scope function) (0) | 2024.12.09 |
[Kotlin] 타입 연산자(is, as), 널 처리 연산자 (0) | 2024.12.08 |