Suche…


Einführung

Reflection ist die Fähigkeit einer Sprache, zur Laufzeit anstelle der Kompilierzeit Code zu prüfen.

Bemerkungen

Reflection ist ein Mechanismus, um Sprachkonstrukte (Klassen und Funktionen) zur Laufzeit zu untersuchen.

Beim Targeting auf die JVM-Plattform werden Laufzeitreflexionsfunktionen in separaten JAR kotlin-reflect.jar verteilt: kotlin-reflect.jar . Dies geschieht, um die Laufzeitgröße zu reduzieren, nicht benötigte Funktionen zu reduzieren und es möglich zu machen, auf andere Plattformen (wie JS) zu zielen.

Eine Klasse referenzieren

Um einen Verweis auf ein KClass Objekt zu erhalten, das eine Klasse darstellt, verwenden Sie Doppelpunkte:

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

Funktion referenzieren

Funktionen sind erstklassige Bürger in Kotlin. Sie können einen Verweis darauf mit Doppelpunkten erhalten und dann an eine andere Funktion übergeben:

fun isPositive(x: Int) = x > 0

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

Interaktion mit Java Reflection

Um ein Java- Class Objekt von KClass verwenden Sie die Erweiterungseigenschaft .java :

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

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

Das letztere Beispiel wird vom Compiler KClass optimiert, dass keine KClass Zwischeninstanz KClass .

Werte aller Eigenschaften einer Klasse abrufen

BaseExample Example , die die BaseExample Klasse mit einigen Eigenschaften erweitert:

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"
}

Man kann alle Eigenschaften einer Klasse erhalten:

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

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

Wenn Sie diesen Code ausführen, wird eine Ausnahme ausgelöst. Die Eigenschaft private val privateField wird als privat deklariert, und der Aufruf von member.get(example) wird nicht erfolgreich durchgeführt. Eine Möglichkeit, damit umzugehen, um private Eigenschaften herauszufiltern. Dazu müssen wir den Sichtbarkeits-Modifizierer des Java-Getters einer Eigenschaft überprüfen. Im Falle von private val der Getter nicht, so dass wir einen privaten Zugriff annehmen können.

Die Hilfsfunktion und ihre Verwendung könnte folgendermaßen aussehen:

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

Ein anderer Ansatz besteht darin, private Immobilien über Reflexion zugänglich zu machen:

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

Einstellungswerte aller Eigenschaften einer Klasse

Als Beispiel möchten wir alle String-Eigenschaften einer Beispielklasse festlegen

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

Das Abrufen von veränderbaren Eigenschaften baut auf dem Abrufen aller Eigenschaften auf, wobei die veränderbaren Eigenschaften nach Typ gefiltert werden. Wir müssen auch die Sichtbarkeit prüfen, da das Lesen privater Eigenschaften zu Laufzeitausnahmen führt.

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

Um alle String Eigenschaften auf "Our Value" , können wir zusätzlich nach dem Rückgabetyp filtern. Da Kotlin auf Java VM basiert, ist Type Erasure in Kraft und daher sind Eigenschaften, die generische Typen wie List<String> , mit List<Any> identisch. Leider ist das Nachdenken keine goldene Kugel, und es gibt keinen vernünftigen Weg, dies zu vermeiden. Daher müssen Sie in Ihren Anwendungsfällen aufpassen.

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow