我正在开展一项任务,创建一个 API 来应用字符串属性,这些属性可以表示为 Enum、Int、String 以及其他一些类型。
所以我尝试编写此类:
object Configuration {
inline fun <reified T> get(context: Context, propertyName: String): T {
val propertyStringValue = context.config[propertyName]
?: throw InvalidConfigurationException("Unable to access property $propertyName")
if (T::class.isSubclassOf(Enum::class)) {
// how can I convert it to enum by it's class name?
} else if (T::class.isSubclassOf(String::class)) {
// convert to string
} else if (T::class.isSubclassOf(Integer::class)) {
// convert it integer
}
}
}
enum class BuildingLevel{
FIRST,
SECOND,
...
}
enum class StreetType {
OPEN,
ISOLATED,
...
}
鉴于属性名称是STREET_TYPE
并context.config["STREET_TYPE"]
返回OPEN
我希望Configuration.get<StreetType>("STREET_TYPE")
返回枚举条目StreetType.OPEN
并Configuration.get<String>("STREET_TYPE")
返回OPEN
字符串。
enum
如果我想将字符串转换为具有代表某个枚举类的具体化泛型的枚举条目,我应该在 if 分支中做什么?
这是强制强类型化弊大于利的情况之一。我们知道它
T
是一个枚举,但是没有办法“强制转换”类型参数或为其添加上限。所以即使我们知道调用enumValueOf<T>(propertyStringValue)
应该没问题,编译器也不会让我们这样做。为了简化,我们可以说任务就是实现这个功能:
我相信从 Kotlin 2.0.20 开始没有干净的方法来实现它。不过还是有一些技巧的。
首先,在 Kotlin 中我们不仅可以抑制许多警告,甚至可以抑制错误:
String
不幸的是,通过抑制错误,我们进入了龙的国度。它可能有效,也可能无效。事实上,此代码在 Kotlin 1.x 中可以正常工作,但它在 2.x 中无法再编译,并出现一些无法编译的愚蠢消息String
。这很可能只是一个错误,将在 Kotlin 的未来版本中修复。我们甚至可以说这个解决方案是最干净的,因为我们只是做了我们必须做的事情,并告诉编译器不要用它的愚蠢限制来打扰我们。但是,它不起作用。enumEntries
类似地,我们可以通过使用/enumValues
代替 来解决上述问题enumValueOf
。这样,我们就可以避免传递String
导致问题的参数:此代码在 Kotlin 2.x 中有效,但它是第一个解决方案的复杂变体。此外,与第一个解决方案类似,它有可能在未来版本的 Kotlin 编译器和/或特定编译目标中停止工作。
最后,我们可以使用反射 API。这可能比简单地通过名称获取枚举更慢,也不那么优雅,但它应该是一种相当可靠的方法:
您必须将 T 修改为 Enum 的子类型,以便可以重用 enumValueOf 函数。