字符串到枚举的Kotlin安全转换

x6492ojm  于 2023-03-19  发布在  Kotlin
关注(0)|答案(6)|浏览(204)

我需要将字符串转换为枚举值,但想要一个函数,返回空值,如果字符串不是一个枚举。

enum class Colors{
   Red, Green, Blue

}

如果testString是value,我可以使用Colors.valueOf(testString),但是如果它无效,就会出现异常,在这种情况下我希望它为null。
因为我经常想这样做,所以扩展函数是理想的。但是扩展需要对类Colors进行操作,而不是类型Colors的对象。
有人知道如何编写这样的扩展吗?理想情况下,它是任何枚举类的通用扩展。
编写一个顶级函数很简单,但我正在寻找一个像标准“方法”那样工作的函数

// instead of 
val willGetAnException = Colors.valueOf("Yellow") // standard existing fun
val willGetNull = Colors.valueOrNullOf("Orange")  // new fun i seek

理想情况下,它是通用的,适用于任何枚举

7vux5j2d

7vux5j2d1#

你不需要扩展,因为它们必须在现有对象上调用。你需要一个顶级函数。有一个内置函数,你可以用途:

/**
 * Returns an enum entry with specified name.
 */
@SinceKotlin("1.1")
public inline fun <reified T : Enum<T>> enumValueOf(name: String): T

您可以通过推断类型来调用它,也可以显式调用它:

val a : MyEnumClass = enumValueOf("A")
val b = enumValueOf<MyEnumClass>("B")

但是,此方法不可为空:对于未知值抛出java.lang.IllegalArgumentException
但是很容易模仿它的行为,让它用一个顶级函数来处理可空枚举:

inline fun <reified T : Enum<*>> enumValueOrNull(name: String): T? =
    T::class.java.enumConstants.firstOrNull { it.name == name }
yebdmbv4

yebdmbv42#

Colors.values().find { it.name == "Yellow" }
zour9fqk

zour9fqk3#

您可以使用类似于以下内容的内容:

inline fun <reified T : Enum<T>> String.asEnumOrDefault(defaultValue: T? = null): T? =
    enumValues<T>().firstOrNull { it.name.equals(this, ignoreCase = true) } ?: defaultValue

然后:"Yellow".asEnumOrDefault(Colors.Green)
或者,如果无法推断:"Yellow".asEnumOrDefault<Colors>()

rsl1atfo

rsl1atfo4#

enum class Colors {
    BLACK, WHITE, UNKNOWN;

    companion object {
        // Verbose for illustrative purposes
        fun fromOrdinal(ordinal: Int): Colors = values()[ordinal]
        fun fromOrdinalOrNull(ordinal: Int): Colors? = values().getOrNull(ordinal)
        fun fromOrdinalOrDefault(ordinal: Int): Colors = values().getOrElse(ordinal) { UNKNOWN }
        fun fromName(name: String): Colors = valueOf(name.uppercase())
        fun fromNameOrNull(name: String): Colors? = values().find { it.name == name.uppercase() }
        fun fromNameOrDefault(name: String): Colors = values().find { it.name == name.uppercase() } ?: UNKNOWN
    }
}
ttvkxqim

ttvkxqim5#

考虑到在Kotlin中安全地访问Enum值并不容易,我发布了一个库enum-or-null-kt来提供一组速记函数,使您可以编写如下代码:

class Example {
  enum class Direction(val az: Int) {
    NORTH(0),
    EAST(90),
    SOUTH(180),
    WEST(240)
  }

  fun printAz01(name: String = "EAST") {
    val direction = enumValueOrNull<Direction>(name) ?: Direction.EAST
    println("az01=${direction.az}")
  }

  fun printAz02(name: String = "EAST") {
    val direction = name.toEnumOrNull<Direction>() ?: Direction.EAST
    println("az02=${direction.az}")
  }

  fun printName01(az: Int = 0) {
    val direction = enumValueOrNull<Direction> {
      it.az == az
    } ?: Direction.NORTH
    println("name03=${direction.name}")
  }

  fun printName02(ordinal: Int = 0) {
    val direction = enumValueOrNull<Direction> {
      it.ordinal == ordinal
    } ?: Direction.NORTH
    println("name03=${direction.name}")
  }
}

使用它,您不仅可以访问带有名称的Enum值,还可以将任意高阶函数作为 predicate 子句传递,这在需要处理自定义转换(如JPA属性转换器)时非常方便。

yebdmbv4

yebdmbv46#

KotlinAPI不能通过简单地使用<reified T: Enum<T>>来工作,它抛出InvocationTargetException类型的异常,所以我直接通过参数传递给type: Class<T>

private fun <T> enumValueOf(type: Class<T>, enum: String) : T {
    return type.enumConstants.first { it.toString() == enum }
}

使用

if (type.isEnum) enumValueOf(#Field.type, value as String)

相关问题