【问题标题】:How do I create an enum from an Int in Kotlin?如何从 Kotlin 中的 Int 创建枚举?
【发布时间】: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)

我得到了错误:

整数文字不符合预期的字符串类型

【问题讨论】:

    标签: kotlin enums


    【解决方案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 }
    }
    

    如果出于可读性或其他原因您更喜欢这样的话。

    如果您想这样做,也可以根据多个参数扩展和检查该功能。这些类型的函数不限于一个参数。

    【讨论】:

    • 在 kotlin 中,一切都旨在是不可变的,因此一切都是副本的副本。在你的代码中到处都有诸如“let”、“filter”、“map”之类的指令,它们中的每一个都在泄漏未使用的对象。缓存枚举值对我来说听起来像是微优化。
    • @AnthonyChatellier 这在技术上是不正确的,它基于 Java 的工作方式。 Kotlin Native 可能并非如此,但 Native 不在此范围内。请参阅stackoverflow.com/a/32799324/6296561 - 缓存可能不会产生太大影响,具体取决于该方法的使用频率,但如果使用足够频繁,它会产生相当不错的影响。不过,您可以不包括缓存
    • 我不知道什么技术上不正确,但我同意你的观点,缓存可能不会产生太大影响,具体取决于该方法的使用频率。例如,在我的笔记本电脑上,在 20 秒内调用 getByValue 6 000 000 000 次甚至没有消耗 1% 的 CPU 用于垃圾收集。所以关于这个结果,即使是一行代码,我也不知道是否值得,对我来说听起来像是微优化。
    • @fvalasiad 不,因为您将值与序数混淆了。如果您添加BAR_BAZ(4738653) 并查找VALUES[4738653],则会抛出,因为VALUES 中只有四个项目。您的解决方案是fun getByIndex(index: Int) = VALUES.getOrNull(index),而不是按价值。如果您想按索引获取很好,但如果您想按价值获取(这就是问题所问的)
    • ...好吧,由于大小检查,它会返回 null,但它不会以任何方式返回项目
    【解决方案2】:

    这适用于希望从 ordinalindex 整数中获取枚举的任何人。

    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

    【讨论】:

      【解决方案3】:

      如果您讨厌为每个 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)
      

      【讨论】:

        【解决方案4】:

        如果您仅使用整数值来维护顺序,您需要访问正确的值,那么您不需要任何额外的代码。您可以使用内置值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 值,以便永远不会发生该错误。
        【解决方案5】:
        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 更改为 it.ordinal
        • @AlanKley it.value 应该引用构造函数中定义的名称。在这种情况下,它是val value: Int。序数是完全不同的东西。这是枚举在列表中的位置。见thisit.ordinal 与引用自定义值不同,这就是这里所做的。如果您输入 2 作为实例,您会得到 FOO_BAR,因为它位于 values[2] 处。 FOO_BAR 的 ordinal 是 2,但 value 是 3。
        【解决方案6】:

        试试这个...

        companion object{
            fun FromInt(v:Int):Type{
                return  Type::class.java.constructors[0].newInstance(v) as Type
            }
        }
        

        【讨论】:

        • 嗨哈尼,欢迎来到 stackoverflow。我建议详细说明您的答案,虽然您的代码应该是正确的,但最好解释一下您在那里做什么和做什么。谢谢。
        • Enum 构造函数是私有的,这是有原因的。即使您可以做到这一点(我真的怀疑),您也不想在其中定义的实例之外创建 Enum 的新实例。不过,此代码对于标准类可能没问题。
        【解决方案7】:

        我会提前构建“反向”地图。可能没有很大的改进,但也没有多少代码。

        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 { ... }
        • @hotkey 非常好!
        • 甚至更短;将.associate { it.value to it } 替换为.associateBy { it.value }
        【解决方案8】:

        这真的取决于你真正想做什么。

        • 如果需要具体的硬编码枚举值,那么可以直接使用Types.FOO
        • 如果您从代码中的其他位置动态接收值,则应尝试直接使用 enum 类型,以免执行此类转换
        • 如果您从 Web 服务接收值,则反序列化工具中应该有允许这种转换的东西(如 Jackson 的 @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)
        

        【讨论】:

          【解决方案9】:

          一种天真的方法可以是:

          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)
          

          【讨论】:

          • 未定义 int 时如何设置值...例如我们抓住还是得到 null ?我想避免。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-05-06
          • 2021-11-13
          • 2015-04-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-14
          相关资源
          最近更新 更多