Расширение полей в Котлине

Легко писать методы расширения в Котлине:

class A { } class B { fun A.newFunction() { ... } } 

Но есть ли способ создать переменную расширения? Подобно:

 class B { var A.someCounter: Int = 0 } 

Нет – в документации объясняется следующее:

Расширения фактически не изменяют классы, которые они расширяют. Определяя расширение, вы не вставляете новые члены в класс, а просто делаете новые функции вызываемыми с точечной нотацией на экземплярах этого класса.

а также

Обратите внимание, что поскольку расширения не вставляют элементы в классы, нет эффективного способа для свойства расширения иметь фоновое поле. Вот почему инициализаторы не допускаются для свойств расширения. Их поведение может быть определено только путем явного предоставления геттеров / сеттеров.

Мысль о функциях / свойствах расширений как просто синтаксическом сахаре для вызова статической функции и передачи в ценности, надеюсь, делает это ясным.

Вы можете создать свойство расширения с переопределенным getter и setter:

 var A.someProperty: Int get() = /* return something */ set(value) { /* do something */ } 

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

Невозможно добавить свойства расширения с полями поддержки в классы, потому что расширения фактически не изменяют класс .

Вы можете определить свойство расширения с помощью настраиваемого getter (и setter для var ) или делегированного свойства .


Однако, если вам нужно определить свойство расширения, которое будет вести себя так, как если бы оно имело поле поддержки, делегированные свойства пригождаются. Идея состоит в том, чтобы создать делегат свойств, который будет хранить сопоставление объектов со значением :

  • используя идентификатор, а не equals() / hashCode() , чтобы фактически хранить значения для каждого объекта, например IdentityHashMap ;

  • не препятствуя тому, чтобы ключевые объекты были собраны в мусор (используя слабые ссылки ), как WeakHashMap делает WeakHashMap .

К сожалению, в JDK нет WeakIdentityHashMap , поэтому вы должны реализовать свою собственную (или выполнить полную реализацию ).

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

 class FieldProperty<R, T : Any>( val initializer: (R) -> T = { throw IllegalStateException("Not initialized.") } ) { private val map = WeakIdentityHashMap<R, T>() operator fun getValue(thisRef: R, property: KProperty<*>): T = map[thisRef] ?: setValue(thisRef, property, initializer(thisRef)) operator fun setValue(thisRef: R, property: KProperty<*>, value: T): T { map[thisRef] = value return value } } 

Пример использования:

 var Int.tag: String by FieldProperty { "$it" } fun main(args: Array<String>) { val x = 0 println(x.tag) // 0 val z = 1 println(z.tag) // 1 x.tag = "my tag" z.tag = x.tag println(z.tag) // my tag } 

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

 private val bATag = FieldProperty<Int, String> { "$it" } class B() { var A.someCounter: Int by FieldProperty { 0 } // independent for each instance of B var A.tag: String by bATag // shared between the instances, but usable only inside B } 

Также обратите внимание, что идентификация не гарантируется для примитивных типов Java из-за бокса.

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

Для поддержки свойств NULL и потокобезопасной реализации см. Здесь .

Вы не можете добавить поле, но вы можете добавить свойство, которое делегирует другим свойствам / методам объекта для реализации его аксессоров (ов). Например, предположим, что вы хотите добавить свойство java.util.Date класс java.util.Date , вы можете написать

 var Date.secondsSinceEpoch: Long get() = this.time / 1000 set(value) { this.time = value * 1000 } 
Intereting Posts
Как я могу создать проект Kotlin, который строится вместе с Gradle? Tornadofx tableview с помощью comboBox и добавление FXEvent на кнопки Почему в Коллекции Котлин нет? Переменная Assert не Null Kotlin JSR-223 ScriptEngineFactory в толстой банке – Не удается найти компилятор kotlin компилятора Java-обработчик аннотации – аннотированные тесты классов классов Kotlin Неправильный вывод для параметра типа reified в Котлине Нужно ли обновлять Kotlin каждый раз, когда я работаю над приложением моей компании? Как я могу получить имя свойства Kotlin? Проблемы с доступом к сопутствующему объекту Kotlin в Groovy? RxKotlin flattenAsObservable (): несоответствие типа с ссылкой на метод Две параллельные строки заменяются в строке kotlin Android Kotlin – адаптер ListView из списка объектов Неправильное отношение типа Corda WireTransaction – TransactionSignature выведено, но DigitalSignature.WithKey ожидается Функция доступа перед вызовом конструктора суперкласса в классе данных Kotlin