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?
. Type
using 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
}
}