如何获得给定密封类的所有子类?

最近,我们将枚举类的一个升级为密封类, 其中的对象作为子类,所以我们可以创建另一个抽象层来简化代码。 然而,我们不能再通过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> 

另一个选择是更复杂的,但它可以满足您的需要,因为密封在同一个包中的课程。 让我告诉你如何以这种方式存档:

  1. 获取你的密封类的URL,例如: ClassLoader.getResource("com/xxx/app/YourSealedClass.class")
  2. 扫描所有jar项目/目录文件在父类的密封类URL,例如: jar://**/com/xxx/appfile://**/com/xxx/app ,然后找出所有的"com/xxx/app/*.class"文件/条目。
  3. 通过使用ClassLoader.loadClass(eachClassName)加载过滤的类
  4. 检查被加载的类是否是你的密封类的子类
  5. 决定如何获取子类实例,例如: Enum.values()object.INSTANCE
  6. 返回所有创建的密封类的实例