Подтвердить что ты не робот

Эффективные перечисления в Котлине с обратным поиском?

Я пытаюсь найти лучший способ сделать "обратный поиск" на перечислении в Котлине. Одним из моих выводов из Effective Java было то, что вы вводите статическую карту внутри enum для обработки обратного поиска. Передача этого в 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]
        }
    }
}

Мой вопрос в том, что это лучший способ сделать это, или есть лучший способ? Что делать, если у меня есть несколько переписей, которые следуют аналогичной схеме? Есть ли способ в Котлине сделать этот код более пригодным для повторного использования в переписках?

4b9b3361

Ответ 1

Прежде всего, аргумент fromInt() должен быть Int, а не Int? , Попытка получить Type с использованием null, очевидно, приведет к нулю, и вызывающая сторона даже не должна пытаться это делать. Map также не имеет причины быть изменчивой. Код может быть уменьшен до:

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

Этот код настолько короткий, что, честно говоря, я не уверен, стоит ли пытаться найти повторно используемое решение.

Ответ 2

В этом случае это не имеет большого значения, но вот "логическое извлечение" для решения @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)

Ответ 3

мы можем использовать find который возвращает первый элемент, соответствующий данному предикату, или null, если такой элемент не найден.

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

Ответ 4

Я обнаружил, что делаю обратный поиск по индивидуальному, ручному кодированию, пару раз и пришел к следующему подходу.

Сделать 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)

Ответ 5

Другой вариант, который можно считать более "идиоматическим", будет следующим:

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

Который затем можно использовать как Type[type].

Ответ 6

Вариант некоторых предыдущих предложений может быть следующим, используя порядковое поле и 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)
    }
}

}

Ответ 7

Еще один пример реализации. Это также устанавливает значение по умолчанию (здесь OPEN), если нет ввода не соответствует опции enum:

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
}

}

Ответ 8

val t = Тип.значения() [порядковый номер]

:)