when fun example enum enums kotlin

enums - example - when function kotlin



Enums efectivos en Kotlin con búsqueda inversa? (7)

Estoy tratando de encontrar la mejor manera de hacer una ''búsqueda inversa'' en una enumeración en Kotlin. Una de mis conclusiones de Effective Java fue que introdujo un mapa estático dentro de la enumeración para manejar la búsqueda inversa. Transmitir esto a Kotlin con una enumeración simple me lleva a un código que se ve así:

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

Mi pregunta es, ¿es esta la mejor manera de hacer esto, o hay una mejor manera? ¿Qué pasa si tengo varias enumeraciones que siguen un patrón similar? ¿Hay alguna forma en Kotlin de hacer que este código sea más reutilizable en las enumeraciones?


En primer lugar, el argumento de fromInt() debería ser un Int , no un Int? . Intentar obtener un Type usando nulo obviamente conducirá a nulo, y una persona que llama ni siquiera debería intentar hacerlo. El Map tampoco tiene motivos para ser mutable. El código se puede reducir a:

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

Ese código es tan corto que, francamente, no estoy seguro de que valga la pena intentar encontrar una solución reutilizable.


Me encontré haciendo la búsqueda inversa por valor personalizado, codificado a mano, valor un par de veces y se me ocurrió el siguiente enfoque.

Haga que enum s implemente una interfaz compartida:

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 }

Esta interfaz (por extraño que sea el nombre :)) marca un cierto valor como código explícito. El objetivo es poder escribir:

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

Lo que se puede lograr fácilmente con el siguiente código:

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)


No tiene mucho sentido en este caso, pero aquí hay una "extracción lógica" para la solución 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 general, eso es lo que pasa con los objetos complementarios que pueden reutilizarse (a diferencia de los miembros estáticos en una clase Java)


Otro ejemplo de implementación. Esto también establece el valor predeterminado (aquí para OPEN ) si la entrada no coincide con ninguna opción de enumeración:

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 }

}


Una variante de algunas propuestas anteriores podría ser la siguiente, utilizando el campo ordinal y 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) } }

}


podemos usar find which Devuelve el primer elemento que coincide con el predicado dado, o nulo si no se encontró dicho elemento.

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


val t = Type.values ​​() [ordinal]

:)