ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 6-1. 특별한 클래스 사용하기 : 이넘(enun) 클래스
    Study(종료)/Kotlin 22.09.13 ~ 12.18 2022. 10. 13. 11:21

    6장

    6-1. 특별한 클래스 사용하기 : 이넘(enun) 클래스

    6-2. 특별한 클래스 사용하기 : 데이터 클래스

    6-3. 특별한 클래스 사용하기 : 인라인 클래스(값 클래스)


    전 단원에 좀 어렵고, 양이 많은 내용들을 작성해서 다음 장에는 덜 어렵고 가벼운 내용들이 나온 것 같다.

    6장에서는 프로그래밍 패턴을 편하게 구현하도록 도와주는 이넘 클래스, 데이터 클래스, 인라인 클래스에 대해 배운다.

     

    6.1 이넘(enum) 클래스

    enum 클래스는 미리 정의된 상수들로 이루어진 제한된 집합을 표현하는 클래스다.

    단순한 형태는 enum class의 본문에 상수를 모아둔 형태다.

    enum class Weekday{
        MON, TUE, WED, THUR, FRI, SAT, SUN
    }
    
    fun Weekday.isWorkDay() =
        this != Weekday.SAT || this != Weekday.SUN
    
    fun main(){
        println(Weekday.MON.isWorkDay()) // true
        println(Weekday.SAT.isWorkDay()) // false
    }

    정수, 문자열 등와 다르게 enum을 사용하면 어떤 변수에 대해서 값을 제한할 수 있게 해준다.

    그래서 변수가 가능한 범위 안에 들어가 있는지 여부를 일일히 확인하지 않아도 된다.

    또한 정해진 상수들로 이뤄진 집합을 type-safe 하게 다루게 해준다.

     

    이넘은 미리 정해진 전역 상수를 정의한다는 점에서 객체 정의와도 유사하다.

    또한 객체와 마찬가지로 전역 상수로 사용할 수 있는 방법이 없는 위치에서는 이넘을 정의할 수 없다.

    예를들어 이넘을 내부 클래스나, 함수 본문에서는 정의할 수 없다.

    fun main(){
        enum class Weekday{ // error: Modifier 'enum' is not applicable to 'local class'
            MON, TUE, WED, THUR, FRI, SAT, SUN
        }
    }

    1) 빠뜨린 부분이 없는 when 식

    when을 사용하면 enum 변수를 각각의 값과 비교할 수 있다.

    when식에서 모든 이넘 상수를 다룬 경우 else를 생략할 수 있다는 하나의 장점이 더 있다.

    이 경우를 빠뜨린 부분이 없는 when식 이라고 부른다.

    빠뜨린 부분이 없는 when식을 사용하면 새 enum 값을 추가하는 경우 깨질 수 있는 코드가 생기는 일을 방지할 수 있다.

    enum class Direction{
        NORTH, SOUTH, WEST, EAST
    }
    
    fun rotateClockWise(direction: Direction) = when (direction){
        Direction.NORTH -> Direction.EAST
        Direction.EAST -> Direction.SOUTH
        Direction.SOUTH -> Direction.WEST
        Direction.WEST -> Direction.NORTH
    }

    내부적으로 빠진 부분이 없는 when 식에는

    NoWhenBranchMatchedException 타입의 예외를 던지는 else 가지가 암시적으로 추가된다.

     


    2) 커스텀 멤버가 있는 이넘 정의하기

    enum 클래스에도 멤버를 포함할 수 있으며 확장 함수나 프로퍼티를 붙일 수 있다.

    enum class Weekday{
        MON, TUE, WED, THU, FRI, SAT, SUN;
    
        val lowerCaseName get() = name.toLowerCase()
        fun isWorkDay() =
            this != SAT || this != SUN
    }
    
    fun main(){
        println(Weekday.MON.isWorkDay()) // true
        println(Weekday.WED.lowerCaseName) // wed
    }

    enum 클래스에 생성자가 있으면 각 이넘 상수의 정의 뒤에서 생성자 호출을 추가해야 한다

    enum class RainbowColor(val isCold: Boolean){
        RED(false), ORANGE(false), YELLOW(false),
        GREEN(true), BLUE(true), INDIGO(true), VIOLET(true);
    
        val isWarm get() = !isCold
    }
    
    fun main(){
        println(RainbowColor.BLUE.isCold) // true
        println(RainbowColor.RED.isWarm) // true
    }

     

    enum 상수에도 자신만의 정의가 들어있는 본문이 포함될 수 있다.

    하지만 이런 상수에 의해 생기는 익명 타입은 코드 밖으로 노출되지 않으므로

    이넘 상수 본문에 정의된 멤버를 해당 본문이 아닌 부분에서 접근하는 것은 불가능하다.

    enum class Weekday{
        MON{fun startWork() = println("Work week start!")},
        TUE, WED, THU, FRI, SAT, SUN;
    }
    
    fun main() = println(Weekday.MON.startWork()) // error: Unresolved reference: startWork

    3) 이넘 클래스의 공통 멤버 사용하기

    코틀린의 모든 이넘 클래스는 암시적으로 kotlin.Enum 클래스의 하위 타입이다.

    이 클래스는 모든 enum 클래스가 사용 가능한 공통 함수와 프로퍼티를 제공한다.

     

    모든 enum 값에는 ordinal과 name 이라는 한 쌍의 프로퍼티가 들어있다.

    ordinal은 이넘 클래스 안에 정의된 이넘 값의 순서에 따른 인덱스이고, name은 이넘 값의 이름이다.

    enum class Direction{
        NORTH, SOUTH, WEST, EAST
        // ordinal : NORTH = 0, SOUTH = 1, WEST = 2, EAST = 3
    }
    
    fun main(){
        println(Direction.NORTH.ordinal) // 0
        println(Direction.NORTH) // NORTH
        println(Direction.NORTH.name) // NORTH
    }

    특정 enum 클래스의 값을 이넘 본문에 있는 위치에 따라 비교할 수 있다. 이넘 값의 동등성은 각각의 식별자에 따라 정해진다. 

    이넘 값에 대한 비교는 ordinal 프로퍼티가 돌려주는 인덱스 값에 의해 정의된다.

    fun main(){
        println(Direction.NORTH == Direction.EAST) // false
        println(Direction.EAST != Direction.SOUTH) // true
        println(Direction.SOUTH > Direction.WEST) // false
        println(Direction.WEST <= Direction.NORTH) // false
    }

    각 이넘 클래스는 동반 객체(companion object) 멤버처럼 호출할 수 있는 암시적 메서드를 제공한다.

    valueOf() 메서드는 enum 값의 이름을 문자열로 넘기면, 그에 해당하는 enum값을 돌려주거나 이름이 잘못된 경우 예외를 던진다.

    fun main(){
        println(Direction.valueOf("NORTH")) // NORTH
        println(Direction.valueOf("EASY")) // runtime error : No enum constant Direction.EASY
    }

    values() 메서드는 정의된 순서대로 모든 enum 값이 들어있는 배열을 돌려준다.

    이 메서드를 호출할 때 마다 배열이 새로 생긴다.

    따라서 이 메서드에서 얻은 배열의 내용을 바꾸더라도 나머지 배열(다른곳에서 values() 메서드를 호출해 얻은)에는 영향이 없다.

    private val weekDays = Weekday.values() // Array<Weekday>
    
    val Weekday.nextDay get() = weekDays[(ordinal + 1) % weekDays.size]
    
    fun main(){
        println(Weekday.TUE.nextDay) // WED
    }

    values()나 valueOf() 대신에 제네릭 최상위 메서드인 enumValues()와 enumBalueOf도 사용할 수 있다.

    fun main(){
        val weekDays = enumValues<Weekday>()
    
        println(weekDays[1]) // TUE
        println(enumValueOf<Weekday>("SUN")) // SUN
    }

    다음글

    6-2. 특별한 클래스 사용하기 : 데이터 클래스

    반응형

    댓글

Designed by Tistory.