最近我们将我们的一个枚举类升级为密封类,并将对象作为子类,因此我们可以进行另一层抽象来简化代码。但是我们不能再通过sealed class State
object StateA: State()
object StateB: State()
object StateC: State()
....// 42 more
函数获取所有可能的子类,这很糟糕,因为我们严重依赖于该功能。有没有办法用反射或任何其他工具检索此类信息?
PS:手动将它们添加到阵列不可接受。目前有45个,并计划增加更多。
这就是我们的密封课程的样子:
val VALUES = setOf(StateA, StateB, StateC, StateC, StateD, StateE,
StateF, StateG, StateH, StateI, StateJ, StateK, StateL, ......
如果有值集合,它将采用以下形状:
{{1}}
当然没有人愿意维持这样一个怪物。
答案 0 :(得分:38)
在Kotlin 1.3+中,您可以使用sealedSubclasses
。
在以前的版本中,如果将子类嵌套在基类中,则可以使用nestedClasses
:
Base::class.nestedClasses
如果在基类中嵌套其他类,则需要添加过滤。 e.g:
Base::class.nestedClasses.filter { it.isFinal && it.isSubclassOf(Base::class) }
请注意,这为您提供了子类,而不是这些子类的实例(与Enum.values()
不同)。
根据您的特定示例,如果State
中的所有嵌套类都是您的object
状态,那么您可以使用以下内容来获取所有实例(例如Enum.values()
):< / p>
State::class.nestedClasses.map { it.objectInstance as State }
如果你想真正想象,你甚至可以使用reflection扩展Enum<E: Enum<E>>
并使用它创建自己的类层次结构到具体对象。 e.g:
sealed class State(name: String, ordinal: Int) : Enum<State>(name, ordinal) {
companion object {
@JvmStatic private val map = State::class.nestedClasses
.filter { klass -> klass.isSubclassOf(State::class) }
.map { klass -> klass.objectInstance }
.filterIsInstance<State>()
.associateBy { value -> value.name }
@JvmStatic fun valueOf(value: String) = requireNotNull(map[value]) {
"No enum constant ${State::class.java.name}.$value"
}
@JvmStatic fun values() = map.values.toTypedArray()
}
abstract class VanillaState(name: String, ordinal: Int) : State(name, ordinal)
abstract class ChocolateState(name: String, ordinal: Int) : State(name, ordinal)
object StateA : VanillaState("StateA", 0)
object StateB : VanillaState("StateB", 1)
object StateC : ChocolateState("StateC", 2)
}
这使得您可以像使用任何其他Enum
一样调用以下内容:
State.valueOf("StateB")
State.values()
enumValueOf<State>("StateC")
enumValues<State>()
<强>更新强>
Kotlin不再支持直接扩展Enum
。看到
Disallow to explicitly extend Enum class : KT-7773
答案 1 :(得分:4)
明智的选择是在kotlin中使用ServiceLoader。然后编写一些提供程序来获取公共类,枚举,对象或数据类实例。例如:
val provides = ServiceLoader.load(YourSealedClassProvider.class).iterator();
val subInstances = providers.flatMap{it.get()};
fun YourSealedClassProvider.get():List<SealedClass>{/*todo*/};
层次结构如下:
Provider SealedClass
^ ^
| |
-------------- --------------
| | | |
EnumProvider ObjectProvider ObjectClass EnumClass
| |-------------------^ ^
| <ueses> |
|-------------------------------------------|
<uses>
另一种选择,更复杂,但它可以满足您的需求,因为密封的类在同一个包中。让我告诉你如何以这种方式存档:
ClassLoader.getResource("com/xxx/app/YourSealedClass.class")
jar://**/com/xxx/app
或file://**/com/xxx/app
,然后查找所有"com/xxx/app/*.class"
个文件/条目。ClassLoader.loadClass(eachClassName)
Enum.values()
,object.INSTANCE
。答案 2 :(得分:3)
使用Kotlin 1.3+,您可以使用反射来列出所有密封的子类,而不必使用嵌套类:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/sealed-subclasses.html
我要求一些功能来实现相同功能而无需反思:https://discuss.kotlinlang.org/t/list-of-sealed-class-objects/10087
答案 3 :(得分:1)
完整示例:
sealed class State{
companion object {
fun find(state: State) =
State::class.sealedSubclasses
.map { it.objectInstance as State}
.firstOrNull { it == state }
.let {
when (it) {
null -> UNKNOWN
else -> it
}
}
}
object StateA: State()
object StateB: State()
object StateC: State()
object UNKNOWN: State()
}