Поиск…


Вступление

Отражение - это способность языка проверять код во время выполнения, а не на время компиляции.

замечания

Отражение - это механизм для интроспекции языковых конструкций (классов и функций) во время выполнения.

При таргетинге на платформу JVM функции отображения времени выполнения распределяются в отдельном JAR: kotlin-reflect.jar . Это делается для уменьшения размера исполняемого файла, сокращения неиспользуемых функций и возможности для таргетинга на другие (например, JS) платформы.

Ссылка на класс

Чтобы получить ссылку на объект KClass представляющий некоторый класс, используйте двойные двоеточия:

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

Ссылка на функцию

Функции - первоклассные граждане в Котлине. Вы можете получить ссылку на нее, используя двойные двоеточия, а затем передать ее другой функции:

fun isPositive(x: Int) = x > 0

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

Взаимодействие с отражением Java

Чтобы получить объект Class Java от KClass Kotlin, используйте свойство расширения .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 объявляется приватным, а вызов member.get(example) на нем не будет выполнен. Один из способов справиться с этим - отфильтровать частные свойства. Для этого нам нужно проверить модификатор видимости Java getter. В случае private val геттер не существует, поэтому мы можем принять частный доступ.

Вспомогательная функция и ее использование могут выглядеть так:

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, тип Erasure действует, и, таким образом, свойства, возвращающие общие типы, такие как 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