Легко написать методы расширения в Котлин:поля расширения в Котлин
class A { }
class B {
fun A.newFunction() { ... }
}
Но есть какой-то способ, чтобы создать переменную расширения? Не как:
class B {
var A.someCounter: Int = 0
}
Легко написать методы расширения в Котлин:поля расширения в Котлин
class A { }
class B {
fun A.newFunction() { ... }
}
Но есть какой-то способ, чтобы создать переменную расширения? Не как:
class B {
var A.someCounter: Int = 0
}
нет - documentation объясняет это:
Extensions фактически не изменять классы они расширяют. Определяя расширение, вы не вставляете новые члены в класс, а просто делаете новые функции вызываемыми с точечной нотацией на экземплярах этого класса.
и
Обратите внимание, что, так как расширения фактически не вставлять элементы в классы, не существует эффективный способ для расширения свойство иметь поля подложки. Вот почему инициализаторы не допускаются для свойств расширения. Их поведение может быть определено только путем явного предоставления геттеров/сеттеров.
Думая о функциях/свойствах расширения как просто синтаксическом сахаре для вызова статической функции и передачи в ценности, мы надеемся, это ясно.
Вы можете создать свойство расширения с перегруженной геттер и сеттер:
var A.someProperty: Int
get() = /* return something */
set(value) { /* do something */ }
Но вы не можете создать свойство расширения с полем подложки, потому что вы не можете добавить поле к существующему классу.
Вы не можете добавить поле, но вы можете добавить свойство, которое делегирует другим свойствам/методам объекта для реализации его аксессоров (ов). Например предположим, что вы хотите добавить secondsSinceEpoch
собственность к java.util.Date
класса, вы можете написать
var Date.secondsSinceEpoch: Long
get() = this.time/1000
set(value) {
this.time = value * 1000
}
Там в no way to add extension properties with backing fields к классам, так как расширения do not actually modify a class.
Свойство расширения можно использовать только с пользовательским геттером (и установщиком для var
) или delegated property.
используя тождество, а не equals()
/hashCode()
, на самом деле хранить значения для каждого объекта, как IdentityHashMap
делает;
не мешать ключевых объектов из сборщика мусора (с использованием weak references), как WeakHashMap
делает.
К сожалению, нет в JDK нет WeakIdentityHashMap
, так что вы должны реализовать свои собственные (или взять complete implementation).
Затем на основе этого сопоставления вы можете создать класс делегата, удовлетворяющий property delegates requirements. Вот пример, не поточно-реализация:
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
}
Кроме того, обратите внимание, что личность is not guaranteed для ПОИ в Java митических типов из-за бокса.
И я подозреваю, что производительность этого решения значительно хуже, чем у обычных полей, скорее всего, близких к нормальным Map
, но это требует дальнейшего тестирования.
Для поддержки свойств с нулевыми свойствами и потокобезопасной реализации обратитесь к here.
Это очень хороший творческий взлом. Вы можете добавить файл лицензии в github. Благодаря! – MadDeveloper