Kotlin 如何获得给定密封类的所有子类?
最近,我们将一个enum类升级为密封类,将对象作为子类,这样我们就可以进行另一层抽象来简化代码。但是,我们不能再通过Kotlin 如何获得给定密封类的所有子类?,kotlin,Kotlin,最近,我们将一个enum类升级为密封类,将对象作为子类,这样我们就可以进行另一层抽象来简化代码。但是,我们不能再通过Enum.values()函数获取所有可能的子类,这是不好的,因为我们严重依赖于该功能。有没有一种方法可以通过反射或任何其他工具检索此类信息 PS:手动将它们添加到数组是不可接受的。目前有45个,并且计划增加更多 这就是我们的密封类的外观: sealed class State object StateA: State() object StateB: State() obje
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
| |-------------------^ ^
| |
|-------------------------------------------|
另一个选项更复杂,但它可以满足您的需要,因为它将类密封在同一个包中。让我告诉您如何以这种方式存档:
ClassLoader.getResource(“com/xxx/app/yourseledclass.class”)
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()
}