Получить KProperty свойства расширения, не относящегося к пакету

#kotlin

#kotlin

Вопрос:

В kotlin вы можете использовать оператор ссылки для получения KProperty свойства расширения пакета следующим образом:

 val String.extProp: String
    get() = "Some get code"

fun foo() {
    val prop: KProperty<String> = String::extProp
}
 

Однако, когда свойство расширения объявляется внутри класса, оператор ссылки больше не работает:

 class Example() {

    val String.extProp: String
        get() = "Some get code"

    fun foo() {
        val prop: KProperty<String> = String::extProp // error
    }

}
 

Итак, мне интересно, как я могу изменить проблемную строку во втором примере, чтобы получить свойство KProperty?

Ответ №1:

Ошибка, которую вы получаете, это:

Ошибка: (y, x) Kotlin: ‘extProp’ является членом и расширением одновременно. Ссылки на такие элементы не допускаются

Не существует синтаксического механизма для генерации ссылки на метод расширения, который также требует содержащего класса. Например, ваше расширение может использовать члены класса, и для этого потребуется что-то вроде «связанных ссылок«, входящих в Kotlin 1.1 (что, я не уверен, также будет охватывать этот случай, в настоящее время это открытый вопрос). Итак, на данный момент синтаксис недоступен :: . Такие вещи, как Example::String::extProp недоступны, также не является часто используемым Example::String.extProp синтаксисом. Но вы можете найти его путем отражения.

Сначала вам нужно знать, какой тип вы получите, это:

 KProperty2<INSTANCE, EXTENDING, PROPTYPE>
 

В то время как обычное свойство класса:

 KProperty1<INSTANCE, PROPTYPE>
 

Вам нужно знать это, поскольку для любого вызова getter потребуется экземпляр класса и экземпляр класса, который расширяет свойство. Таким образом, вы не можете вызывать его так же, как ссылку на свойство класса.

Вы можете использовать эту функцию для поиска свойства расширения, объявленного в классе:

 @Suppress("UNCHECKED_CAST")
fun <T: Any, EXTENDING: Any, R: Any> KClass<T>.extProp(extends: KClass<EXTENDING>, name: String, returning: KClass<R>): KProperty2<T, EXTENDING, R> {
    return this.declaredMemberExtensionProperties.first {
        it.name == name amp;amp;
                it.parameters.size == 2 amp;amp;
                it.parameters[0].kind == KParameter.Kind.INSTANCE amp;amp; it.parameters[0].type == this.defaultType amp;amp;
                it.parameters[1].kind == KParameter.Kind.EXTENSION_RECEIVER amp;amp; it.parameters[1].type == extends.defaultType amp;amp;
                it.returnType == returning.defaultType
    } as KProperty2<T, EXTENDING, R>
}
 

Это немного излишне для проверки, но гарантирует, что оно будет надежным в будущем на случай, если позже будут добавлены какие-либо другие типы расширений. Ниже приведен ваш код, обновленный для его использования:

 class Example() {
    val String.extProp: String
        get() = "howdy $this"

    fun foo() {
        val prop = Example::class.extProp(String::class, "extProp", String::class)
        println(prop.get(this, "stringy"))  // "howdy stringy"
    }
}