E 'facile scrivere metodi di estensione in Kotlin:campi di estensione in Kotlin
class A { }
class B {
fun A.newFunction() { ... }
}
Ma c'è qualche modo per creare variabili estensione? Come:
class B {
var A.someCounter: Int = 0
}
E 'facile scrivere metodi di estensione in Kotlin:campi di estensione in Kotlin
class A { }
class B {
fun A.newFunction() { ... }
}
Ma c'è qualche modo per creare variabili estensione? Come:
class B {
var A.someCounter: Int = 0
}
No - l'documentation spiega questo:
estensioni in realtà non modificano le classi che estendono. Definendo un'estensione, non si inseriscono nuovi membri in una classe, ma si limitano a rendere nuove funzioni richiamabili con la notazione a punti sulle istanze di questa classe.
e
Si noti che, dal momento che le estensioni in realtà non inserire i membri in classi, non c'è modo efficace per una proprietà di estensione di avere un campo di supporto. Questo è il motivo per cui gli inizializzatori non sono consentiti per le proprietà di estensione. Il loro comportamento può essere definito solo fornendo getter/setter in modo esplicito.
Pensare alle funzioni/proprietà di estensione come semplice zucchero sintattico per chiamare una funzione statica e passare in un valore sperabilmente lo rende chiaro.
È possibile creare una proprietà di estensione con getter e setter override:
var A.someProperty: Int
get() = /* return something */
set(value) { /* do something */ }
Ma non si può creare una proprietà di estensione con un campo di supporto perché non è possibile aggiungere un campo a una classe esistente.
Non è possibile aggiungere un campo, ma è possibile aggiungere una proprietà che delega ad altre proprietà/metodi dell'oggetto per implementare i relativi accessor. Per esempio si supponga di voler aggiungere una proprietà secondsSinceEpoch
alla classe java.util.Date
, è possibile scrivere
var Date.secondsSinceEpoch: Long
get() = this.time/1000
set(value) {
this.time = value * 1000
}
C'è no way to add extension properties with backing fields alle classi, perché le estensioni do not actually modify a class.
È possibile definire una proprietà di interno solo con getter personalizzato (e setter per var
) o delegated property.
utilizzando l'identità, non equals()
/hashCode()
, effettivamente memorizzare i valori per ogni oggetto, come IdentityHashMap
fa;
non impedire i chiave oggetti vengano rifiuti raccolti (usando weak references), come WeakHashMap
fa.
Purtroppo, non c'è WeakIdentityHashMap
nel JDK, quindi bisogna implementare il proprio (o prendere un complete implementation).
Quindi, in base a questa mappatura è possibile creare una classe delegato che soddisfi lo property delegates requirements. Ecco un esempio di attuazione non-thread-safe:
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
}
}
Esempio di utilizzo:
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
}
Quando definita all'interno di una classe, la mappatura può essere memorizzato indipendentemente per istanze della classe o in una condiviso oggetto delegato:
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
}
Inoltre, si ricorda che l'identità is not guaranteed per PRI di Java tipi mitivi dovuti al pugilato.
E sospetto che le prestazioni di questa soluzione siano significativamente peggiori rispetto a quelle dei campi regolari, molto probabilmente vicino al normale Map
, ma ciò richiede ulteriori test.
Per il supporto di proprietà nullable e l'implementazione thread-safe, fare riferimento a here.
questo è un bel trucco creativo. È possibile aggiungere un file di licenza al github. Grazie! – MadDeveloper