【发布时间】:2019-04-30 15:02:23
【问题描述】:
我有这个enum:
enum class Types(val value: Int) {
FOO(1)
BAR(2)
FOO_BAR(3)
}
如何使用Int 创建该enum 的实例?
我试着做这样的事情:
val type = Types.valueOf(1)
我得到了错误:
整数文字不符合预期的字符串类型
【问题讨论】:
我有这个enum:
enum class Types(val value: Int) {
FOO(1)
BAR(2)
FOO_BAR(3)
}
如何使用Int 创建该enum 的实例?
我试着做这样的事情:
val type = Types.valueOf(1)
我得到了错误:
整数文字不符合预期的字符串类型
【问题讨论】:
Enum#valueOf is based on name。这意味着为了使用它,您需要使用valueof("FOO")。 valueof 方法因此需要一个字符串,它解释了错误。 String 不是 Int,类型很重要。我也提到它的作用是为了让您知道这不是您要寻找的方法。
如果您想根据 int 值获取一个,您需要定义自己的函数来执行此操作。您可以使用values() 获取枚举中的值,在这种情况下返回Array<Types>。您可以使用firstOrNull 作为安全方法,如果您更喜欢异常而不是 null,则可以使用first。
所以添加一个伴随对象(相对于枚举是静态的,因此您可以通过Types.FOO.getByValue(1234) 调用Types.getByValue(1234)(Java 中的Types.COMPANION.getByValue(1234))。
companion object {
private val VALUES = values()
fun getByValue(value: Int) = VALUES.firstOrNull { it.value == value }
}
values() 每次调用时都会返回一个新数组,这意味着您应该在本地缓存它以避免每次调用getByValue 时都重新创建一个。如果您在调用该方法时调用values(),则可能会反复重新创建它(但具体取决于您实际调用它的次数),这会浪费内存。
诚然,正如 cmets 中所讨论的,这可能是一个微不足道的优化,具体取决于您的使用情况。这意味着您还可以:
companion object {
fun getByValue(value: Int) = values().firstOrNull { it.value == value }
}
如果出于可读性或其他原因您更喜欢这样的话。
如果您想这样做,也可以根据多个参数扩展和检查该功能。这些类型的函数不限于一个参数。
【讨论】:
BAR_BAZ(4738653) 并查找VALUES[4738653],则会抛出,因为VALUES 中只有四个项目。您的解决方案是fun getByIndex(index: Int) = VALUES.getOrNull(index),而不是按价值。如果您想按索引获取很好,但如果您想按价值获取(这就是问题所问的)
这适用于希望从 ordinal 或 index 整数中获取枚举的任何人。
enum class MyEnum { RED, GREEN, BLUE }
MyEnum.values()[1] // GREEN
另一种解决方案及其变体:
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>()[i]
enumFromIndex<MyEnum>(1) // GREEN
inline fun <reified T : Enum<T>> enumFromIndex(i: Int) = enumValues<T>().getOrNull(i)
enumFromIndex<MyEnum>(3) ?: MyEnum.RED // RED
inline fun <reified T : Enum<T>> enumFromIndex(i: Int, default: T) =
enumValues<T>().getOrElse(i) { default }
enumFromIndex(2, MyEnum.RED) // BLUE
它是another answer 的改编版本。另外,感谢 Miha_x64 this answer。
【讨论】:
如果您讨厌为每个 enum 声明,请键入 companion object{ ... } 以实现 EMotorcycleType.fromInt(...)。这里有一个解决方案。
EnumCaster 对象:
object EnumCaster {
inline fun <reified E : Enum<E>> fromInt(value: Int): E {
return enumValues<E>().first { it.toString().toInt() == value }
}
}
枚举示例:
enum class EMotorcycleType(val value: Int){
Unknown(0),
Sport(1),
SportTouring(2),
Touring(3),
Naked(4),
Enduro(5),
SuperMoto(6),
Chopper(7),
CafeRacer(8),
.....
Count(9999);
override fun toString(): String = value.toString()
}
使用示例1:Kotlin enum to jni and back
fun getType(): EMotorcycleType = EnumCaster.fromInt(nGetType())
private external fun nGetType(): Int
fun setType(type: EMotorcycleType) = nSetType(type.value)
private external fun nSetType(value: Int)
---- or ----
var type : EMotorcycleType
get() = EnumCaster.fromInt(nGetType())
set(value) = nSetType(value.value)
private external fun nGetType(): Int
private external fun nSetType(value: Int)
使用示例2:赋值给val
val type = EnumCaster.fromInt<EMotorcycleType>(aValidTypeIntValue)
val typeTwo : EMotorcycleType = EnumCaster.fromInt(anotherValidTypeIntValue)
【讨论】:
如果您仅使用整数值来维护顺序,您需要访问正确的值,那么您不需要任何额外的代码。您可以使用内置值ordinal。序数表示值在枚举声明中的位置。
这是一个例子:
enum class Types {
FOO, //Types.FOO.ordinal == 0 also position == 0
BAR, //Types.BAR.ordinal == 1 also position == 1
FOO_BAR //Types.FOO_BAR.ordinal == 2 also position == 2
}
您只需调用即可访问序数值:
Types.FOO.ordinal
要获得正确的枚举值,您只需调用:
Types.values()[0] //Returns FOO
Types.values()[1] //Returns BAR
Types.values()[2] //Returns FOO_BAR
Types.values() 按照声明的顺序返回枚举值。
总结:
Types.values(Types.FOO.ordinal) == Types.FOO //This is true
如果整数值与顺序不匹配 (int_value != enum.ordinal) 或者您使用不同的类型(字符串、浮点数...),那么您需要迭代并比较您的自定义值,因为它已经在这个话题。
【讨论】:
enum 中的 int 值,以便永远不会发生该错误。
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
fun fromInt(value: Int) = Types.values().first { it.value == value }
}
}
您可能希望为范围添加安全检查并返回 null。
【讨论】:
it.value 应该引用构造函数中定义的名称。在这种情况下,它是val value: Int。序数是完全不同的东西。这是枚举在列表中的位置。见this。 it.ordinal 与引用自定义值不同,这就是这里所做的。如果您输入 2 作为实例,您会得到 FOO_BAR,因为它位于 values[2] 处。 FOO_BAR 的 ordinal 是 2,但 value 是 3。
试试这个...
companion object{
fun FromInt(v:Int):Type{
return Type::class.java.constructors[0].newInstance(v) as Type
}
}
【讨论】:
Enum 构造函数是私有的,这是有原因的。即使您可以做到这一点(我真的怀疑),您也不想在其中定义的实例之外创建 Enum 的新实例。不过,此代码对于标准类可能没问题。
我会提前构建“反向”地图。可能没有很大的改进,但也没有多少代码。
enum class Test(val value: Int) {
A(1),
B(2);
companion object {
val reverseValues: Map<Int, Test> = values().associate { it.value to it }
fun valueFrom(i: Int): Test = reverseValues[i]!!
}
}
编辑:map...toMap() 根据@hotkey 的建议更改为associate。
【讨论】:
.map { ... }.toMap()替换为.associate { ... }。
.associate { it.value to it } 替换为.associateBy { it.value }
这真的取决于你真正想做什么。
Types.FOO
@JsonValue)value 属性),那么恐怕您必须实现自己的转换方法,正如@Zoe 指出的那样。
实现这种自定义转换的一种方法是通过转换方法添加伴随对象:
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
private val types = values().associate { it.value to it }
fun findByValue(value: Int) = types[value]
}
}
Kotlin 中的伴随对象旨在包含属于该类但不绑定到任何实例的成员(如 Java 的 static 成员)。
在那里实现该方法允许您通过调用来访问您的值:
var bar = Types.findByValue(2)
【讨论】:
一种天真的方法可以是:
enum class Types(val value: Int) {
FOO(1),
BAR(2),
FOO_BAR(3);
companion object {
fun valueOf(value: Int) = Types.values().find { it.value == value }
}
}
然后就可以使用了
var bar = Types.valueOf(2)
【讨论】: