Kotlin - эффективный и масштабируемый способ получить значение свойства экземпляра класса по id или по строке
Я хотел бы сделать свойства класса и его дочерних классов доступными во время выполнения для чтения и записи через целочисленный идентификатор или по имени свойства с производительностью, максимально приближенной к производительности обычного скомпилированного чтения или записи, насколько это возможно. Может быть много экземпляров этого класса и его дочерних классов (скажем, до 1 миллиона), и каждый класс может иметь сотни свойств, поэтому я хочу минимизировать объем памяти, используемый каждым свойством в каждом экземпляре класса.
Широкие группы решений, которые я вижу, используют рефлексию, превращая каждое свойство в экземпляр изменяемого класса, а затем сохраняют их карты или пишут гигантские операторы when.
Я проверил производительность реализации отражения (см. Ниже). Это занимает в 15 раз больше времени, чем прямой доступ к свойству в моем тесте.
Можно ли это улучшить или есть лучший способ сделать это?
class ReflectionClass {
@FieldId(1)
var intField = 0
fun getPropById(id: Int): Any? {
val property = propertiesById[id]
return property?.get(this)
}
fun setIntPropById(id: Int, value: Int) {
val property = propertiesById[id]
if (property is KMutableProperty1) {
property?.setter?.call(this, value)
}
}
fun getPropByName(name: String): Any? {
val property = propertiesByName[name]
return property?.get(this)
}
fun setIntPropByName(name: String, value: Int) {
val property = propertiesByName[name]
if (property is KMutableProperty1) {
property as KMutableProperty1<ReflectionClass, Int>
property.set(this, value)
}
}
companion object {
//private val propertiesById = HashMap<Int, KProperty1<ReflectionClass,*>>()
private val propertiesById = HashMap<Int, KProperty1<ReflectionClass, *>?>()
private val propertiesByName = HashMap<String, KProperty1<ReflectionClass, *>>()
init {
val fields = ReflectionClass::class.memberProperties.forEach { property ->
val id = property.findAnnotation<FieldId>()
if (id != null) {
propertiesById.put(id.id, property)
propertiesByName.put(property.name, property)
}
}
}
}
}
1 ответ
Я не думаю, что вы получите представление, которое вы хотите от размышлений.
(Reflection не был предназначен для высокопроизводительного использования - и, по моему опыту, очень редко используется в производственном коде. Он отлично подходит для тестирования, каркасов, инструментов сборки и т. Д., Но в большинстве вопросов, которые я вижу по этому поводу, реальный ответ - использовать лучший дизайн, который не нуждается в отражении!)
Конечно, ни один из двух других подходов не идеален. Это, вероятно, зависит от точных требований. Должны ли они вообще быть объектами с именованными свойствами Kotlin или они могут быть простыми картами? Последний может быть проще для кода и проще в обслуживании. Остальные жестко запрограммированные тесты, вероятно, спасут память.
(Если бы у вас было много времени, вы могли бы написать какой-то инструмент для сборки, который мог бы автоматически генерировать метод поиска с этими жестко закодированными для вас тестами. Это, конечно, использовало бы отражение, но только во время компиляции. Хотя я буду большой работой, и я не знаю, как вы к ней подойдете.)