코틀린 언어 정리 2-7

enum class

C/C++ 에서 열거형 상수로 지원하던 것을 코틀린에서는 열거형 객체로 지원합니다.


구조 설명

enum class 내부에 선언된 enum constant(열거형 상수)는 모두 enum class를 상속받은 1회성 클래스의 인스턴스입니다.(object declaration으로 생성되는 객체로 볼 수 있음) 따라서 초기화 시 enum class의 주생성자 호출 형식을 사용하며 enum class에 선언한 프로퍼티나 함수는 enum class 내부에 선언한 열거형 상수 객체에도 모두 적용됩니다. 그리고 enum class에는 열거형 상수의 이름을 저장하는 name과, 열거형 상수를 선언한 순서대로 0부터 1씩 증가 시킨 값을 저장하는 ordinal 프로퍼티가 자동으로 추가되고, 초기화 됩니다.


선언 방법

크게 3가지 선언 방법이 있습니다. 

  • C/C++의 enum 처럼 ","(콤마)로 구분하여 열거형 상수의 이름만 선언하는 방법

  • 열거형 상수의 이름과 프로퍼티의 초기 값을 직접 선언하는 방법

  • 열거형 상수를 이름없는 클래스(Anonymous Class) 선언 방식으로 선언하는 방법


예제

// 열거형 상수의 이름만 선언하는 방법

enum class Direction {

   NORTH, SOUTH, EAST, WEST;

}


// 열거형 상수의 이름과 프로퍼티의 초기 값을 직접 선언하는 방법

enum class Color(val rgb: Int) {

   RED(0xFF0000),

   GREEN(0x00FF00),

   BLUE((0x0000FF))

}


// Anonymous Class 초기화 방식 사용. 이 방식으로 getOpposite, getKoreanName, nNumberCode 등과 같이 enum constants에 대해 개별적으로 멤버 함수나 프로퍼티를 임의로 추가할 수 있습니다.

enum class Direction1 {

   NORTH_ {

       var nNumberCode: Int = 10

       override fun getOpposite(): Direction1 = SOUTH_

   },

   SOUTH_ {

       fun getKoreanName (): String { return "남" }

       override fun getOpposite(): Direction1 = NORTH_

   },

   EAST_ {

       var nNumberCode: Int = 30

       override fun getOpposite() = WEST_

   },

   WEST_ {

       override fun getOpposite() = EAST_

       fun getKoreanName (): String { return "서" }

   };


   abstract fun getOpposite(): Direction1

}



제약

  • enum class 는 interface를 상속받을 수 있지만 class는 상속 받을 수 없습니다.

  • enum class에 멤버 함수나 멤버 프로퍼티 선언은 enum constants 선언 이후에 추가해야 합니다.

  • enum constants 내부에 개별적으로 추가한 멤버는 enum class 외부에서 참조할 수 없습니다. 엄밀히 말해 이는 enum constants만의 제약이 아니라 object class의 특성입니다. 따라서 enum constants에 추가한 멤버를 외부에서 참조하려면, enum constants에 포함한 멤버와 동일한 선언의 멤버를 enum class, 또는 enum class가 상속받는 interface에 선언하고 enum constants에서 이를 override 해야 합니다.


(자동으로)제공되는 것들

우선 설명을 위해 enum class와 enum constants가 다음 예제와 같이 정의되어 있다고 가정합니다.


예제

enum class Direction {

   NORTH, WEST, SOUTH, EAST

}


용어가 가리키는 것

enum class => Direction

enum constants => NORTH, WEST, SOUTH, EAST


일반 클래스와 비교했을 때 자동으로 제공되는 프로퍼티나 함수들이 있습니다. 위 예제에 정의된 enum class나 enum constants를 이용하여 설명하면 아래와 같습니다.

  • enum class에는 name: String, ordinal: Int 프로퍼티가 자동으로 추가됩니다.

  • enum constants를 참조하기 위해 다음의 두 함수(values, valueOf)와 이에 대응하는 제네릭 함수(enumValues, enumValueOf)를 사용할 수 있습니다.

  • enum class에 values():Array<Direction>, valueOf(name:String):Direction 두 개의 함수가 제공됩니다.

    • Direction.values():Array<Direction>

      • T enum class에 정의된 모든 enum constants를 배열로 만들어 리턴합니다. ( 동일한 클래스에서 호출한 values()가 === 연산자로 비교했을 때 일치하지 않는 것으로 보아 함수를 호출할 때마다 배열 객체(Array<T>)를 새로 생성하는 것으로 보임.
        ex. Direction.values() === Direction.values() => false )

    • Direction.valueOf(name:String):T

      • name으로 enum constant를 찾는다.(enum constant의 name속성으로 검색)

  • enumValues<Direction>():Array<Direction> 제네릭 함수가 제공됩니다. 이 함수는 위에서 설명한 Direction.values():Array<Direction>에 대응합니다.

  • enumValueOf(name:String):Direction 제네릭 함수가 제공됩니다. 이 함수는 위에서 설명한 Direction.valueOf(name:String):Direction에 대응합니다.


예제

enum class Direction {

   NORTH, WEST, SOUTH, EAST

}


fun test () {

   println(Direction.values() == Direction.values()) // 결과값이 false입니다. 함수를 호출할 때마다 Array객체를 새로 생성하는 것으로 보인다.

   println(Direction.values() === enumValues<Direction>()) // 결과값이 false입니다.

   println(Direction.values()[0] === enumValues<Direction>()[0]) // true...배열에서 리턴되는 enum constant는 모두 동일함

   println(Direction.values()[1] === enumValues<Direction>()[1]) // true...배열에서 리턴되는 enum constant는 모두 동일함

   println(Direction.values()[2] === enumValues<Direction>()[2]) // true...배열에서 리턴되는 enum constant는 모두 동일함

   println(Direction.values()[3] === enumValues<Direction>()[3]) // true...배열에서 리턴되는 enum constant는 모두 동일함

   println(Direction.valueOf("NORTH") === enumValueOf<Direction>("NORTH")) // true

}



전체 예제

다음은 위에서 언급한 내용을 포함한 전체 예제입니다.


예제

enum class Direction {

   NORTH, SOUTH, EAST, WEST; // 열거형 상수의 이름만 선언하는 방법


   init {

       println(name) // 자동으로 추가되고 초기화된 name 프로퍼티

       println(ordinal) // 자동으로 추가되고 초기화된 ordinal 프로퍼티

   }

}


enum class Color(val rgb: Int) {

   RED(0xFF0000), // 각각의 열거형 상수 객채(enum constant object)는 Color를 상속 받았으며 초기화도 Color의 주생성자와 동일한 형식으로 합니다.

   GREEN(0x00FF00),

   BLUE((0x0000FF))

}


enum class Direction1 {

   NORTH_ { // Anonymous Class 초기화 방식 사용

       override fun getOpposite(): Direction1 = SOUTH_

   },

   SOUTH_ {

       override fun getOpposite(): Direction1 = NORTH_

   },

   EAST_ {

       fun getKoreanName (): String { return "동" } // 개별적인 멤버 함수 추가 가능

       override fun getOpposite() = WEST_

   },

   WEST_ {

       var sAdjacentSeaKoreanName = "서해" // 개별적인 프로퍼티 추가 가능

       override fun getOpposite() = EAST_

   };


   // 멤버 함수나 멤버 프로퍼티 선언은 enum constants 선언 이후에 추가해야 합니다.

   abstract fun getOpposite(): Direction1 // Direction1의 멤버로 추상함수를 정의하면 Direction1을 상속 받는 각각의 enum constants 에서 이 함수를 반드시 구현해야 합니다.

   var name1: String = "unknown"

}


interface IDirection2 {

   fun getOpposite(): Direction2

}


enum class Direction2 : IDirection2 {

   NORTH_ { // Anonymous Class

       override fun getOpposite(): Direction2 = SOUTH_

   },

   SOUTH_ {

       override fun getOpposite(): Direction2 = NORTH_

   },

   EAST_ {

       override fun getOpposite() = WEST_

   },

   WEST_ {

       override fun getOpposite() = EAST_

   };

}


inline fun <reified T : Enum<T>> printAllEnumConstantProps() {

   println(enumValues<T>().joinToString { it.name + ":" + it.ordinal + " " })

}


//fun main(args: Array<String>) {

fun test () {

   var dir: Direction = Direction.SOUTH

   println("${dir.name}, ${dir.ordinal}")

   println(dir)

   println("${Direction.values().joinToString { it.name + ":" + it.ordinal + " " }}")

   println("${enumValues<Direction>().joinToString { it.name + ":" + it.ordinal + " " }}")

   printAllEnumConstantProps<Direction>()

   println(Direction.valueOf("SOUTH"))



   var clr: Color = Color.RED

   println("$clr ${clr.name}, ${clr.ordinal}, ${clr.rgb}")

   println("${Color.values().joinToString { it.name + ":" + it.ordinal + " " }}")

   println("${enumValues<Color>().joinToString { it.name + ":" + it.ordinal + " " }}")

   printAllEnumConstantProps<Color>()

   println(Color.valueOf("BLUE").ordinal)

   println(enumValueOf<Color>("BLUE").ordinal)



   var dir1: Direction1 = Direction1.EAST_

   println("$dir1, ${dir1.getOpposite()}")

   println("${Direction1.values().joinToString { it.name + ":" + it.ordinal + " " }}")

   println("${enumValues<Direction1>().joinToString { it.name + ":" + it.ordinal + " " }}")

   printAllEnumConstantProps<Direction1>()

   println(enumValueOf<Direction1>("WEST_").getOpposite())


   var dir2: Direction2 = Direction2.SOUTH_

   println("${dir2.getOpposite()}")

}


'코틀린( Kotlin )' 카테고리의 다른 글

코틀린 2-9 Nested / Inner class  (0) 2020.04.06
코틀린 2-8 sealed class  (0) 2020.04.06
코틀린 2-6 data class  (0) 2020.04.05
코틀린 2-5 interface  (0) 2020.04.05
코틀린 2-4 class - 생성자 호출 순서, Any  (0) 2020.04.05

+ Recent posts