2017-06-27 9 views
11

最近ではenumクラスの1つをsealedクラスにオブジェクトをサブクラスとしてアップグレードしました。これにより、コードを単純化するための別の層の抽象化を行うことができます。しかし、我々はもはや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, ...... 

当然誰も維持したいと考えていませんそのような怪物。

+0

私の知る限りでは、実際にここに進めるための唯一*許容*方法です。あなたは与えられたクラスのすべての可能なサブクラスを知る方法がありません。これはJVMが実際にどのように動作するかの制限です。私が間違っているなら、私を修正してください。 – m0skit0

+1

[関連](https://youtrack.jetbrains.com/issueMobile/KT-14657) – HTNW

+0

達成しようとしていることは何ですか?いくつかのコードを投稿できますか? – D3xter

答えて

12

あなたは巣お使いのベースクラスのサブクラスは、あなたが使用できる場合nestedClasses

Base::class.nestedClasses 

場合は、お使いのベースクラス内のネスト他のクラスフィルタリングを追加する必要があります。例えば:これは(Enum.values()とは違って)あなたのサブクラスではなく、それらのサブクラスのインスタンスを与えること

Base::class.nestedClasses.filter { it.isFinal && it.isSubclassOf(Base::class) } 

は注意してください。あなたの特定の例では


Stateであなたのネストされたクラスのすべてがあなたのobject状態であれば、あなたはすべてのインスタンス(のようなEnum.values())を取得するには、以下を使用することができます。

State::class.nestedClasses.map { it.objectInstance as State } 

そして、あなたが本当に気に入らなければならない場合は、Enum<E: Enum<E>>を拡張してreflectionを使用して、それからあなたの具体的なオブジェクトまで独自のクラス階層を作成することもできます。例えば:

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>() 
+0

KT-14657(ありがとう@HTNW)の直前に解決する必要があります。インスタンスについては、 'KClass'に' objectInstance'プロパティがあることがわかりました。 – glee8e

+0

@ glee8e素晴らしい。私はそれが助けてうれしいです。更新された質問とあなたのコメントに基づいて回答を更新しました。 – mfulton26

+1

私が好きな人は、isSubclassOfがどこから来ているのか分かりません。それはkotlinがapiを反映しているからです。 org.jetbrains.kotlinを追加してください:kotlin-reflect:1.1.4-3 "'をあなたのbuild.gradleの依存関係に追加してください – Defuera

1

賢明な選択は、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. は例えば、密閉されたクラスのURLの親内のすべてのJARエントリ/ディレクトリのファイルをスキャン:jar://**/com/xxx/appまたはfile://**/com/xxx/appすべてのファイル/エントリ"com/xxx/app/*.class"を見つけてください。 ClassLoader.loadClass(eachClassName)
  3. ロードされたクラスを確認するを使用して、
  4. 負荷フィルタクラスにかかわら例えば、サブクラスのインスタンスを取得する方法をあなたの密閉されたクラス
  5. のサブクラスを決定している:Enum.values()object.INSTANCE
  6. リターン設立密封されたクラスのインスタンスのすべてが
+0

これは、たとえ文字通りそうでないとしても、「配列を手作業で配列に追加する」ことを意味します。 – m0skit0

+0

@ m0skit0はい、確かに、第1のアプローチは手動である。それは維持目的のためです。論理を分割することができるからです。 –

+0

はい、質問は「アレイに手動で追加することは容認できません」と記載されています。 – m0skit0

関連する問題