如何获得给定密封类的所有子类?
最近,我们将枚举类的一个升级为密封类, 其中的对象作为子类,所以我们可以创建另一个抽象层来简化代码。 然而,我们不能再通过Enum.values()
函数获得所有可能的子类,这是不好的,因为我们严重依赖于这个功能。 有没有办法通过反射或其他工具检索这些信息?
PS:手动添加到阵列是不可接受的 。 目前有45个,并计划增加更多。
这就是我们的密封课程的样子:
sealed class State object StateA: State() object StateB: State() object StateC: State() ....// 42 more
如果有一个值集合,它将是这样的形状:
val VALUES = setOf(StateA, StateB, StateC, StateC, StateD, StateE, StateF, StateG, StateH, StateI, StateJ, StateK, StateL, ......
当然,没有人想要维护这样一个怪物。
如果将子类嵌套在基类中,则可以使用nestedClasses
:
Base::class.nestedClasses
如果你在你的基类中嵌套其他类,那么你需要添加过滤。 例如:
Base::class.nestedClasses.filter { it.isFinal && it.isSubclassOf(Base::class) }
请注意,这给你的子类,而不是这些子类的实例(不像Enum.values()
)。
用你特定的例子,如果你在State
中的所有嵌套类都是你的object
状态,那么你可以使用下面的方法获得所有的实例(如Enum.values()
):
State::class.nestedClasses.map { it.objectInstance as State }
如果你想变得很花哨,甚至可以扩展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) }
这使得它可以像其他任何Enum
一样调用以下内容:
State.valueOf("StateB") State.values() enumValueOf<State>("StateC") enumValues<State>()
明智的选择是在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>
另一个选择是更复杂的,但它可以满足您的需要,因为密封在同一个包中的课程。 让我告诉你如何以这种方式存档:
- 获取你的密封类的URL,例如:
ClassLoader.getResource("com/xxx/app/YourSealedClass.class")
- 扫描所有jar项目/目录文件在父类的密封类URL,例如:
jar://**/com/xxx/app
或file://**/com/xxx/app
,然后找出所有的"com/xxx/app/*.class"
文件/条目。 - 通过使用
ClassLoader.loadClass(eachClassName)
加载过滤的类 - 检查被加载的类是否是你的密封类的子类
- 决定如何获取子类实例,例如:
Enum.values()
,object.INSTANCE
。 - 返回所有创建的密封类的实例