2016-09-10 11 views
3

Как я могу получить класс делегирования свойства-члена?Kotlin: Как я могу получить класс делегирования свойства члена?

Под этим я имею в виду, что можно выполнить такую ​​функцию:

inline fun <reified T> delegationExample(t: T) { 
    for (prop in T::class.declaredMemberProperties) { 
     val delegatedClass = // what to do?! 
    } 
} 

Если класс делегация может выглядеть следующим образом:

class DelegationExample { 
    operator fun getValue(ref: Any, prop: KProperty<*>) = 0 
} 

И объявление класса может выглядеть следующим образом:

object Example { 
    val a by DelegationExample() 
    val b by DelegationExample() 
    val c by DelegationExample() 
} 

ответ

3

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

data class DelegatedProperty<T : Any, DELEGATE : Any>(val property: KProperty1<T, *>, val delegatingToInstance: DELEGATE) 

inline fun <reified T : Any, DELEGATE : Any> findDelegatingPropertyInstances(instance: T, delegatingTo: KClass<DELEGATE>): List<DelegatedProperty<T, DELEGATE>> { 
    return T::class.declaredMemberProperties.map { prop -> 
     val javaField = prop.javaField 
     if (javaField != null && delegatingTo.java.isAssignableFrom(javaField.type)) { 
      javaField.isAccessible = true // is private, have to open that up 
      @Suppress("UNCHECKED_CAST") 
      val delegateInstance = javaField.get(instance) as DELEGATE 
      DelegatedProperty(prop, delegateInstance) 
     } else { 
      null 
     } 
    }.filterNotNull() 
} 

Несколько замечаний:

  • Сначала исправить ваш реифицированные тип T до T: Any или вы не можете получить доступ ко всем расширениям в отражении Котлин, включая declaredMemberProperties
  • Проще всего добраться до поля из ссылки на свойство, чтобы убедиться, что вы действительно говорите о чем-то, что действительно является свойством, поэтому для каждого из declaredMemberProperties используйте javaField для этого.
  • С javaField - это пользовательский геттер и может быть нулевым, он сохраняется в локальной переменной, поэтому умное кастинг будет работать позже.
  • Тогда, если это поле имеет тот же тип, что и класс делегирования, который вы ищете, вы можете получить доступ к полю.
  • Но сначала вы должны принудительно получить доступ к полю, потому что это поле private.

Запуск этого в тестовой программе:

class DelegationExample { 
    operator fun getValue(ref: Any, prop: KProperty<*>) = 0 
} 

class Example { 
    val a by DelegationExample() 
    val b by DelegationExample() 
    val c by DelegationExample() 
} 

fun main(args: Array<String>) { 
    findDelegatingPropertyInstances(Example(), DelegationExample::class).forEach { 
     println("property '${it.property.name}' delegates to instance of [${it.delegatingToInstance}]") 
    } 
} 

Выход что-то вроде:

property 'a' delegates to instance of [[email protected]] 
property 'b' delegates to instance of [[email protected]] 
property 'c' delegates to instance of [[email protected]] 
+0

Мой патч: https://gist.github.com/Jire/df9c99fac5e4d3f35e1a6c517716f989 – Jire

+1

Конечно, оба делают подобные вещи. Я хотел вернуть все вещи, которые делегируют в этом примере, а также не называть 'isAccessible' на все, что у меня не было (есть ли влияние на вызов его больше?) –

+0

Патч, который вы ссылаетесь (Jire), будет работать, только если это только одно объявленное свойство, которое использует делегат, который передается как второй параметр. Нужно ли найти делегата для данного свойства, если есть два или более свойств с использованием одного и того же делегата? Я хотел бы иметь функцию, которая могла бы это сделать. Я попытался понять это, но я не думаю, что это возможно. –

1

На уровне байт-кода делегированные свойства не откладываются от обычных (publi c getter/setter и частное поле).

Один из способов, по которым вы могли бы пойти, сканировать частные поля Example и фильтровать те, которые имеют operator getValue(thisRef: R, KProperty<*>). Технически поле может содержать объект-делегат val x = lazy {1}, но это маловероятно.