Котлин получает доступ к имени свойства от делегата

#kotlin #compilation #reflect

#kotlin #Сборник #отразить

Вопрос:

Я пишу систему сохранения для своей игры, используя делегированные свойства kotlin.

Вот небольшой пример

 import kotlin.reflect.KProperty

object Test {
    val test: Int by no_prop { 10 } // I want this
    val testTwo: Int by prop(::testTwo) { 10 } // I don't want this if possible
}

inline fun <reified T : Any> Test.no_prop(initializer: () -> T) = TestProp<T>()

inline fun <reified T : Any> Test.prop(prop: KProperty<*>, initializer: () -> T): TestProp<T> {
    println("My saving key is ${this.javaClass.simpleName}.${prop.name}")
    return TestProp<T>()
}

class TestProp<T : Any> {
    operator fun getValue(thisRef: Any?, property: KProperty<*>) = 10 as T
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {}
}
  

Моя игра использует строковый ключ для сохраненных свойств. Его всегда *callerClassName*.*propertyName*

Мне было интересно, возможно ли через мои функции расширения делегата или TestProp класс для доступа к имени свойства делегирует?

Пример: Было бы неплохо, если бы метод no_prop мог, чтобы свойство, которое его вызывало, было названо «test»

 val test: Int by no_prop { 10 }
  

Ниже приведен обходной путь, но я бы хотел избежать этого для каждого свойства, если это возможно

 val testTwo: Int by prop(::testTwo) { 10 }
  

Ответ №1:

Вы можете. Всякий раз, когда вы создаете делегат, вы реализуете ReadOnlyProperty (или ReadWriteProperty ):

 public fun interface ReadOnlyProperty<in T, out V> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public operator fun getValue(thisRef: T, property: KProperty<*>): V
}
  

При попытке получить доступ к значению свойства для делегирования ( val test: Int by no_prop { 10 } в вашем случае) getValue будет вызвана функция, и у вас будет доступ к KProperty . В нем есть name поле, которое вы можете использовать для этой цели.

Это работает и для свойств класса, и для локальных свойств!

Комментарии:

1. Да, именно так я делаю это сейчас, но мне нужно было сделать это раньше, когда свойство создается. У меня возникла проблема, когда к такому свойству, как UID / GUID, не обращаются / часто не используются и не сохраняются, поскольку к нему никогда не обращались. Смотрите Мой ответ для решения.

2. Я понимаю. Это не то, что вы спрашивали.

Ответ №2:

Оказывается, у kotlin есть метод «provideDelategate», который вызывается при создании делегированного свойства. См. https://kotlinlang.org/docs/reference/delegated-properties.html#providing-a-delegate

Работает следующий код:

 import kotlin.reflect.KProperty

object Test {
    val test: Int by prop { 10 } 
}

inline fun <reified T : Any> Test.prop(prop: KProperty<*>, initializer: () -> T) = TestProp<T>()

class TestProp<T : Any> {
    operator fun getValue(thisRef: Any?, property: KProperty<*>) = 10 as T
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {}

    // THIS IS CALLED ON INITIAL CREATION
    operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): TestProp<T> {
        println("My saving key is ${thisRef.javaClass.simpleName}.${property.name}")
        return this
    }
}

fun main() {
    println(Test.test)
}