145 votes

Enumération efficace en Kotlin avec recherche inversée?

J'essaie de trouver la meilleure façon de faire une "recherche inversée" sur un enum dans Kotlin. Un de mes plats à emporter à partir Efficaces de Java, a été d'introduire une carte statique à l'intérieur de l'enum pour gérer la recherche inversée. Le portage de ce fil de Kotlin avec un simple enum m'amène à un code qui ressemble à ceci:

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]
        }
    }
}

Ma question est, est-ce la meilleure façon de le faire, ou est-il un meilleur moyen? Que faire si j'ai plusieurs énumérations qui suivent un schéma similaire? Est-il possible de Kotlin pour rendre ce code plus re-utilisable à travers des énumérations?

231voto

JB Nizet Points 250258

Tout d'abord, l'argument de fromInt () doit être un Int, pas un Int ?. Essayer d'obtenir un Type en utilisant null mènera évidemment à null, et un appelant ne devrait même pas essayer de le faire. La carte n'a également aucune raison d'être mutable. Le code peut être réduit à

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

Ce code est si court que, franchement, je ne suis pas sûr que cela vaut la peine d'essayer de trouver une solution réutilisable.

60voto

humazed Points 398

nous pouvons utiliser find qui renvoie le premier élément correspondant au prédicat donné, ou null si aucun élément de ce type n'a été trouvé.

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

28voto

voddan Points 16

Cela n'a pas beaucoup de sens dans ce cas, mais voici une "extraction logique" pour la solution de @ 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
 

En général, les objets compagnons peuvent être réutilisés (contrairement aux membres statiques d'une classe Java).

6voto

miensol Points 1889

Je me suis retrouvé à faire la recherche inversée par la coutume, codé à la main, valeur deux fois et est venu avec l'approche suivante.

Assurez-vous que enum s implémente une interface partagée:

 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
}
 

Cette interface (aussi étrange que son nom soit :)) marque une certaine valeur en tant que code explicite. Le but est de pouvoir écrire:

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

Ce qui peut être facilement réalisé avec le code suivant:

 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)
 

-1voto

shmulik.r Points 107

val t = Type.values () [ordinal]

:)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X