2016-04-08 3 views
15

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

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

Но есть какой-то способ, чтобы создать переменную расширения? Не как:

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

ответ

24

нет - documentation объясняет это:

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

и

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

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

11

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

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

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

5

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

var Date.secondsSinceEpoch: Long 
    get() = this.time/1000 
    set(value) { 
     this.time = value * 1000 
    } 
7

Там в 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.

+0

Это очень хороший творческий взлом. Вы можете добавить файл лицензии в github. Благодаря! – MadDeveloper

Смежные вопросы