サーチ…


前書き

リフレクションとは、コンパイル時ではなく実行時にコードを検査する言語の能力です。

備考

リフレクションは、実行時に言語構造(クラスおよび関数)をイントロスペクションするためのメカニズムです。

JVMプラットフォームを対象とする場合、実行時リフレクション機能は別々のJAR: kotlin-reflect.jarます。これは実行時のサイズを減らし、未使用の機能をカットし、別の(JSのような)プラットフォームをターゲットにすることを可能にするために行われます。

クラスの参照

いくつかのクラスを表すKClassオブジェクトへの参照を取得するには、二重コロンを使用します。

val c1 = String::class
val c2 = MyClass::class

関数の参照

関数はKotlinの一流市民です。二重コロンを使用して参照を取得し、別の関数に渡すことができます。

fun isPositive(x: Int) = x > 0

val numbers = listOf(-2, -1, 0, 1, 2)
println(numbers.filter(::isPositive)) // [1, 2]

Javaリフレクションとの相互運用

KotlinのKClassからJavaのClassオブジェクトを取得するには、 .java拡張プロパティを使用します。

val stringKClass: KClass<String> = String::class
val c1: Class<String> = stringKClass.java

val c2: Class<MyClass> = MyClass::class.java

後者の例は、中間のKClassインスタンスを割り当てないようにコンパイラによって最適化されます。

クラスのすべてのプロパティの値を取得する

与えられたExample拡張クラスBaseExampleいくつかのプロパティを持つクラスを:

open class BaseExample(val baseField: String)

class Example(val field1: String, val field2: Int, baseField: String): 
    BaseExample(baseField) {
    
    val field3: String
        get() = "Property without backing field"

    val field4 by lazy { "Delegated value" }

    private val privateField: String = "Private value"
}

クラスのすべてのプロパティを保持することができます:

val example = Example(field1 = "abc", field2 = 1, baseField = "someText")

example::class.memberProperties.forEach { member ->
    println("${member.name} -> ${member.get(example)}")
}

このコードを実行すると、例外がスローされます。プロパティprivate val privateFieldはprivateと宣言され、その上でmember.get(example)を呼び出すと成功しません。これを処理してプライベートプロパティを除外する1つの方法。これを行うには、プロパティのJavaゲッターの可視性変更子をチェックする必要があります。 private valの場合、getterは存在しないため、プライベートアクセスが可能です。

ヘルパー関数とその使用法は次のようになります:

fun isFieldAccessible(property: KProperty1<*, *>): Boolean {
    return property.javaGetter?.modifiers?.let { !Modifier.isPrivate(it) } ?: false
}

val example = Example(field1 = "abc", field2 = 1, baseField = "someText")

example::class.memberProperties.filter { isFieldAccessible(it) }.forEach { member ->
    println("${member.name} -> ${member.get(example)}")
}

別のアプローチは、リフレクションを使用してプライベートプロパティにアクセスできるようにすることです。

example::class.memberProperties.forEach { member ->
    member.isAccessible = true
    println("${member.name} -> ${member.get(example)}")
}

クラスのすべてのプロパティの値を設定する

例として、サンプルクラスのすべての文字列プロパティを設定する

class TestClass {
    val readOnlyProperty: String
        get() = "Read only!"

    var readWriteString = "asd"
    var readWriteInt = 23

    var readWriteBackedStringProperty: String = ""
        get() = field + '5'
        set(value) { field = value + '5' }

    var readWriteBackedIntProperty: Int = 0
        get() = field + 1
        set(value) { field = value - 1 }

    var delegatedProperty: Int by TestDelegate()

    private var privateProperty = "This should be private"

    private class TestDelegate {
        private var backingField = 3

        operator fun getValue(thisRef: Any?, prop: KProperty<*>): Int {
            return backingField
        }

        operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: Int) {
            backingField += value
        }
    }
}

変更可能なプロパティを取得するには、すべてのプロパティを取得し、変更可能なプロパティをタイプ別にフィルタリングします。プライベートプロパティを読み取ると実行時例外が発生するため、可視性もチェックする必要があります。

val instance = TestClass()
TestClass::class.memberProperties
        .filter{ prop.visibility == KVisibility.PUBLIC }
        .filterIsInstance<KMutableProperty<*>>()
        .forEach { prop ->
            System.out.println("${prop.name} -> ${prop.get(instance)")
        }

すべてのStringプロパティを"Our Value"に設定するには、戻り値の型でさらにフィルタリングできます。 KotlinはJava VMをベースにしているため、 タイプ消去が有効なので、 List<String>などの汎用タイプを返すプロパティはList<Any>と同じになります。残念ながら、反射は黄金の弾丸ではなく、これを避けるための賢明な方法はないので、あなたの使用事例で注意する必要があります。

val instance = TestClass()
TestClass::class.memberProperties
        .filter{ prop.visibility == KVisibility.PUBLIC }
        // We only want strings
        .filter{ it.returnType.isSubtypeOf(String::class.starProjectedType) }
        .filterIsInstance<KMutableProperty<*>>()
        .forEach { prop ->
            // Instead of printing the property we set it to some value
            prop.setter.call(instance, "Our Value")
        }


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow