最近我们将我们的一个枚举类升级为带有对象作为子类的密封类,因此我们可以进行另一层抽象来简化代码.但是我们不能再通过Enum.values()函数获取所有可能的子类,这很糟糕,因为我们非常依赖于该功能.有没有办法用反射或任何其他工具检索此类信息?
PS:手动将它们添加到阵列是不可接受的.目前有45个,并计划增加更多.
这就是我们的密封类的样子:
sealed class State
object StateA: State()
object StateB: State()
object StateC: State()
....// 42 more
Run Code Online (Sandbox Code Playgroud)
如果有值集合,它将采用以下形状:
val VALUES = setOf(StateA, StateB, StateC, StateC, StateD, StateE,
StateF, StateG, StateH, StateI, StateJ, StateK, StateL, ......
Run Code Online (Sandbox Code Playgroud)
当然没有人想要保持这样的怪物.
mfu*_*n26 43
在Kotlin 1.3+中你可以使用sealedSubclasses.
在以前的版本中,如果将子类嵌套在基类中,则可以使用nestedClasses:
Base::class.nestedClasses
Run Code Online (Sandbox Code Playgroud)
如果在基类中嵌套其他类,则需要添加过滤.例如:
Base::class.nestedClasses.filter { it.isFinal && it.isSubclassOf(Base::class) }
Run Code Online (Sandbox Code Playgroud)
请注意,这为您提供了子类,而不是这些子类的实例(与此不同Enum.values()).
使用您的特定示例,如果您的所有嵌套类State都是您的object状态,那么您可以使用以下内容来获取所有实例(例如Enum.values()):
State::class.nestedClasses.map { it.objectInstance as State }
Run Code Online (Sandbox Code Playgroud)
如果你想真正想要的话,你甚至可以Enum<E: Enum<E>>使用反射扩展并创建自己的类层次结构,从它到具体的对象.例如:
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)
}
Run Code Online (Sandbox Code Playgroud)
这使得您可以像其他任何一样调用以下内容Enum:
State.valueOf("StateB")
State.values()
enumValueOf<State>("StateC")
enumValues<State>()
Run Code Online (Sandbox Code Playgroud)
UPDATE
EnumKotlin不再支持直接扩展.请参见
禁止显式扩展Enum类:KT-7773.
完整示例:
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()
}
Run Code Online (Sandbox Code Playgroud)
使用 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
一个明智的选择是在 kotlin 中使用ServiceLoader。然后写一些提供者来获取一个通用的类、枚举、对象或数据类实例。例如:
val provides = ServiceLoader.load(YourSealedClassProvider.class).iterator();
val subInstances = providers.flatMap{it.get()};
fun YourSealedClassProvider.get():List<SealedClass>{/*todo*/};
Run Code Online (Sandbox Code Playgroud)
层次结构如下:
Provider SealedClass
^ ^
| |
-------------- --------------
| | | |
EnumProvider ObjectProvider ObjectClass EnumClass
| |-------------------^ ^
| <uses> |
|-------------------------------------------|
<uses>
Run Code Online (Sandbox Code Playgroud)
另一种选择更复杂,但它可以满足您的需求,因为在同一个包中密封了类。让我告诉你如何以这种方式存档:
ClassLoader.getResource("com/xxx/app/YourSealedClass.class")jar://**/com/xxx/appor file://**/com/xxx/app,然后找出所有"com/xxx/app/*.class"文件/条目。ClassLoader.loadClass(eachClassName)Enum.values(), object.INSTANCE。| 归档时间: |
|
| 查看次数: |
8223 次 |
| 最近记录: |