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

Kotlin 如何获得给定密封类的所有子类?,kotlin,Kotlin,最近,我们将一个enum类升级为密封类,将对象作为子类,这样我们就可以进行另一层抽象来简化代码。但是,我们不能再通过Enum.values()函数获取所有可能的子类,这是不好的,因为我们严重依赖于该功能。有没有一种方法可以通过反射或任何其他工具检索此类信息 PS:手动将它们添加到数组是不可接受的。目前有45个,并且计划增加更多 这就是我们的密封类的外观: sealed class State object StateA: State() object StateB: State() obje

最近,我们将一个enum类升级为密封类,将对象作为子类,这样我们就可以进行另一层抽象来简化代码。但是,我们不能再通过
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, ......
自然没有人想养这样一只怪物。

在科特林使用它是明智的选择。然后编写一些提供程序来获取公共类、枚举、对象或数据类实例。例如:

val provides = ServiceLoader.load(YourSealedClassProvider.class).iterator();

val subInstances =  providers.flatMap{it.get()};

fun YourSealedClassProvider.get():List<SealedClass>{/*todo*/};
val提供=ServiceLoader.load(yourseledclassprovider.class).iterator();
val subInstances=providers.flatMap{it.get()};
fun yourselatedClassProvider.get():List{/*todo*/};
层次结构如下所示:

                Provider                    SealedClass
                   ^                             ^
                   |                             |
            --------------                --------------
            |            |                |            |
        EnumProvider ObjectProvider    ObjectClass  EnumClass
            |            |-------------------^          ^
            |                    <uses>                 |
            |-------------------------------------------|
                                 <uses>
Provider-SealedClass
^                             ^
|                             |
--------------                --------------
|            |                |            |
EnumProvider对象提供程序对象类EnumClass
|            |-------------------^          ^
|                                     |
|-------------------------------------------|

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

  • 获取密封类的URL,例如:
    ClassLoader.getResource(“com/xxx/app/yourseledclass.class”)
  • 扫描密封类URL父级中的所有jar条目/目录文件,例如:
    jar://**/com/xxx/app
    文件://**/com/xxx/app
    ,然后找出所有
    “com/xxx/app/*.class”
    文件/条目
  • 使用
    ClassLoader.loadClass(eachClassName)
  • 检查加载的类是否是密封类的子类
  • 决定如何获取子类实例,例如:
    Enum.values()
    object.instance
  • 返回已建立的密封类的所有实例

  • 在Kotlin 1.3+中,您可以使用

    在以前的版本中,如果将子类嵌套在基类中,则可以使用:

    如果在基类中嵌套其他类,则需要添加筛选。e、 g:

    Base::class.nestedClasses.filter { it.isFinal && it.isSubclassOf(Base::class) }
    
    请注意,这将为您提供子类,而不是这些子类的实例(不同于
    Enum.values()


    在您的特定示例中,如果
    状态中的所有嵌套类都是
    对象状态,那么您可以使用以下方法获取所有实例(如
    Enum.values()
    ):


    如果你真的想变得更花哨,你甚至可以扩展
    Enum
    ,并使用它创建你自己的类层次结构到你的具体对象。e、 g:


    更新

    Kotlin不再支持直接扩展
    Enum
    。看见
    .

    使用Kotlin 1.3+可以使用反射列出所有密封的子类,而不必使用嵌套类:

    我要求提供一些功能来实现相同的功能,而无需思考:

    完整示例:

    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()
    
    }
    

    AFAIK将它们手动添加到数组中实际上是唯一可以接受的方法。您无法知道给定类的所有可能的子类。这是JVM实际工作方式的限制。如果我错了,请纠正我。你想达到什么目的?您能发布一些代码吗?@m0skit0实际上有这样做的,但是对于这种情况来说它似乎太重了。@D3xter获取给定密封类的所有子类,如标题中所述,其用途是替换Enum.values。有些问题不需要代码就可以理解,这就是其中之一。如果你真的担心的话,我还是添加了一些代码。这仍然意味着“手动将它们添加到数组中”,即使不是字面意思:)@m0skit0是的,先生。事实上,第一种方法是手动的。但这是为了维护。因为您可以将逻辑拆分为多个部分。是的,但问题指出“手动将它们添加到数组中是不可接受的”。@m0skit0是的,先生。然后我给他另一个复杂的选择。谢谢你复杂的回答。这实际上是因为它提供了更好的灵活性,但我们认为这可能会对性能产生很大影响(扫描整个类路径听起来很糟糕)。我们宁愿让它们都是嵌套类,并使用mfulton26的解决方案。在KT-14657(谢谢@HTNW)得到解决之前,我们应该考虑这个问题。关于这个例子,我们设法找到了
    KClass
    获得了
    objectInstance
    属性。对于像我这样的人来说,他们无法立即找出
    isSubclassOf
    来自哪里-这是kotlin reflect api。在build.gradle dependenciesThanks@Defuera中添加
    compile“org.jetbrains.kotlin:kotlin reflect:1.1.4-3”
    ,我已经添加了类似枚举的解决方案需要反射的说明。谢谢。@HendraAnggrian,显然从Kotlin 1.1.4开始这就不可能了-(@mfulton26感谢您的澄清。这真是一个遗憾,这是一个非常巧妙的技巧。此功能非常有用,但对于任何考虑使用此功能的人来说,请注意它可能非常慢,因为它使用反射。
    State::class.nestedClasses.map { it.objectInstance as State }
    
    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)
    }
    
    State.valueOf("StateB")
    State.values()
    enumValueOf<State>("StateC")
    enumValues<State>()
    
    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()
    
    }