[enums] 역방향 조회를 사용하는 Kotlin의 효과적인 열거 형?

Kotlin의 열거 형에서 ‘역방향 조회’를 수행하는 가장 좋은 방법을 찾으려고합니다. Effective Java에서 얻은 내용 중 하나는 역방향 조회를 처리하기 위해 열거 형 내부에 정적 맵을 도입했다는 것입니다. 간단한 열거 형을 사용하여 이것을 Kotlin으로 포팅하면 다음과 같은 코드가 표시됩니다.

enum class Type(val value: Int) {
    A(1),
    B(2),
    C(3);

    companion object {
        val map: MutableMap<Int, Type> = HashMap()

        init {
            for (i in Type.values()) {
                map[i.value] = i
            } 
        }

        fun fromInt(type: Int?): Type? {
            return map[type]
        }
    }
}

제 질문은 이것이 최선의 방법입니까, 아니면 더 나은 방법이 있습니까? 비슷한 패턴을 따르는 열거 형이 여러 개 있으면 어떻게됩니까? Kotlin에이 코드를 열거 형에서 더 재사용 할 수있는 방법이 있습니까?



답변

우선의 인수는 fromInt()이어야 Int합니다 Int?. Typeusing null 을 얻으려고 시도 하면 분명히 null로 이어질 것이며 호출자는 그것을 시도하지 않아야합니다. 는 Map또한 변경 가능 할 이유가 없습니다. 코드는 다음과 같이 줄일 수 있습니다.

companion object {
    private val map = Type.values().associateBy(Type::value)
    fun fromInt(type: Int) = map[type]
}

이 코드는 너무 짧아서 솔직히 재사용 가능한 솔루션을 찾을 가치가 있는지 모르겠습니다.


답변

우리가 사용할 수 find있는 , 이러한 요소가 발견 된 경우 먼저 주어진 조건에 일치하는 요소를 돌려줍니다.

companion object {
   fun valueOf(value: Int): Type? = Type.values().find { it.value == value }
}


답변

이 경우에는 의미가 없지만 @JBNized 솔루션에 대한 “논리 추출”이 있습니다.

open class EnumCompanion<T, V>(private val valueMap: Map<T, V>) {
    fun fromInt(type: T) = valueMap[type]
}

enum class TT(val x: Int) {
    A(10),
    B(20),
    C(30);

    companion object : EnumCompanion<Int, TT>(TT.values().associateBy(TT::x))
}

//sorry I had to rename things for sanity

일반적으로 재사용 할 수있는 동반 객체에 대한 것입니다 (Java 클래스의 정적 멤버와 달리)


답변

더 “특이한”것으로 간주 될 수있는 또 다른 옵션은 다음과 같습니다.

companion object {
    private val map = Type.values().associateBy(Type::value)
    operator fun get(value: Int) = map[value]
}

그런 다음 Type[type].


답변

나는 사용자 정의, 수작업 코딩, 값으로 역방향 조회를 몇 번 수행하고 다음과 같은 접근 방식을 생각해 냈습니다.

확인 enum의 공유 인터페이스를 구현 :

interface Codified<out T : Serializable> {
    val code: T
}

enum class Alphabet(val value: Int) : Codified<Int> {
    A(1),
    B(2),
    C(3);

    override val code = value
}

이 인터페이스 (이름이 이상하더라도 :)) 는 특정 값을 명시 적 코드로 표시합니다. 목표는 다음과 같이 작성할 수있는 것입니다.

val a = Alphabet::class.decode(1) //Alphabet.A
val d = Alphabet::class.tryDecode(4) //null

다음 코드로 쉽게 얻을 수 있습니다.

interface Codified<out T : Serializable> {
    val code: T

    object Enums {
        private val enumCodesByClass = ConcurrentHashMap<Class<*>, Map<Serializable, Enum<*>>>()

        inline fun <reified T, TCode : Serializable> decode(code: TCode): T where T : Codified<TCode>, T : Enum<*> {
            return decode(T::class.java, code)
        }

        fun <T, TCode : Serializable> decode(enumClass: Class<T>, code: TCode): T where T : Codified<TCode> {
            return tryDecode(enumClass, code) ?: throw IllegalArgumentException("No $enumClass value with code == $code")
        }

        inline fun <reified T, TCode : Serializable> tryDecode(code: TCode): T? where T : Codified<TCode> {
            return tryDecode(T::class.java, code)
        }

        @Suppress("UNCHECKED_CAST")
        fun <T, TCode : Serializable> tryDecode(enumClass: Class<T>, code: TCode): T? where T : Codified<TCode> {
            val valuesForEnumClass = enumCodesByClass.getOrPut(enumClass as Class<Enum<*>>, {
                enumClass.enumConstants.associateBy { (it as T).code }
            })

            return valuesForEnumClass[code] as T?
        }
    }
}

fun <T, TCode> KClass<T>.decode(code: TCode): T
        where T : Codified<TCode>, T : Enum<T>, TCode : Serializable
        = Codified.Enums.decode(java, code)

fun <T, TCode> KClass<T>.tryDecode(code: TCode): T?
        where T : Codified<TCode>, T : Enum<T>, TCode : Serializable
        = Codified.Enums.tryDecode(java, code)


답변

서수 필드 및 getValue를 사용하는 이전 제안의 변형은 다음과 같습니다.

enum class Type {
A, B, C;

companion object {
    private val map = values().associateBy(Type::ordinal)

    fun fromInt(number: Int): Type {
        require(number in 0 until map.size) { "number out of bounds (must be positive or zero & inferior to map.size)." }
        return map.getValue(number)
    }
}

}


답변

또 다른 구현 예. OPEN입력이 열거 형 옵션과 일치하지 않는 경우 기본값 (여기서는 ) 도 설정합니다 .

enum class Status(val status: Int) {
OPEN(1),
CLOSED(2);

companion object {
    @JvmStatic
    fun fromInt(status: Int): Status =
        values().find { value -> value.status == status } ?: OPEN
}

}