Recherche…


Introduction

La réflexion est la capacité d'un langage à inspecter le code à l'exécution au lieu de la compilation.

Remarques

Reflection est un mécanisme d'introspection des constructions de langage (classes et fonctions) au moment de l'exécution.

Lors du ciblage de la plate-forme JVM, les fonctionnalités de réflexion à l'exécution sont distribuées dans un kotlin-reflect.jar JAR distinct: kotlin-reflect.jar . Ceci est fait pour réduire la taille de l'exécution, réduire les fonctionnalités inutilisées et permettre de cibler d'autres plateformes (comme JS).

Référencement d'une classe

Pour obtenir une référence à un objet KClass représentant des doubles KClass utilisation de classe:

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

Référencement d'une fonction

Les fonctions sont des citoyens de première classe à Kotlin. Vous pouvez obtenir une référence à l'aide de deux points et la transmettre à une autre fonction:

fun isPositive(x: Int) = x > 0

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

Interaction avec la réflexion Java

Pour obtenir un objet de Class Java à partir du KClass de Kotlin, utilisez la propriété d’extension .java :

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

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

Le dernier exemple sera optimisé par le compilateur pour ne pas allouer une instance KClass intermédiaire.

Obtenir des valeurs de toutes les propriétés d'une classe

Étant donné la classe Example qui BaseExample classe BaseExample avec certaines propriétés:

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

On peut s'emparer de toutes les propriétés d'une classe:

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

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

L'exécution de ce code entraînera la levée d'une exception. La propriété private val privateField est déclarée privée et l'appel de member.get(example) ne réussira pas. Une façon de gérer cela pour filtrer les propriétés privées. Pour ce faire, nous devons vérifier le modificateur de visibilité du getter Java d'une propriété. En cas de valeur private val le getter n'existe pas, nous pouvons donc supposer un accès privé.

La fonction d'assistance et son utilisation peuvent ressembler à ceci:

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

Une autre approche consiste à rendre les propriétés privées accessibles en utilisant la réflexion:

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

Définition des valeurs de toutes les propriétés d'une classe

A titre d'exemple, nous voulons définir toutes les propriétés de chaîne d'une classe d'échantillon

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

Obtenir des propriétés modifiables s'appuie sur l'obtention de toutes les propriétés, en filtrant les propriétés mutables par type. Nous devons également vérifier la visibilité, car la lecture des propriétés privées entraîne une exception d'exécution.

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

Pour définir toutes les propriétés de String sur "Our Value" nous pouvons également filtrer par le type de retour. Étant donné que Kotlin est basé sur la machine virtuelle Java, Type Erasure est activé et les propriétés renvoyant des types génériques tels que List<String> seront donc identiques à List<Any> . Malheureusement, la réflexion n'est pas une solution miracle et il n'y a pas de moyen sensé d'éviter cela. Vous devez donc faire attention à vos cas d'utilisation.

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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow