initialize findview extensions extension kotlin

findview - Campos de extensión en Kotlin



kotlin extension (4)

Es fácil escribir métodos de extensión en Kotlin:

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

Pero, ¿hay alguna manera de crear la variable de extensión? Me gusta:

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


No hay forma de agregar propiedades de extensión con campos de respaldo a las clases, porque las extensiones no modifican realmente una clase .

Solo puede definir una propiedad de extensión con un captador personalizado (y un configurador para var ) o una propiedad delegada .

Sin embargo, si necesita definir una propiedad de extensión que se comporte como si tuviera un campo de respaldo, las propiedades delegadas son útiles. La idea es crear un delegado de propiedad que almacene la asignación de objeto a valor :
  • usar la identidad, no equals() / hashCode() , para almacenar realmente los valores para cada objeto, como lo hace IdentityHashMap ;

  • no impide que los objetos clave se recojan (utilizando referencias débiles ), como hace WeakHashMap .

Desafortunadamente, no hay WeakIdentityHashMap en JDK, por lo que debe implementar el suyo propio (o tomar una implementación completa ).

Luego, en función de esta asignación, puede crear una clase de delegado que satisfaga los requisitos de los delegados de propiedad . Aquí hay un ejemplo de implementación no segura para subprocesos:

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 } }

Ejemplo de uso:

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 }

Cuando se define dentro de una clase, la asignación se puede almacenar de forma independiente para las instancias de la clase o en un objeto delegado compartido:

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 }

Además, tenga en cuenta que la identidad no está garantizada para los tipos primitivos de Java debido al boxeo.

Y sospecho que el rendimiento de esta solución es significativamente peor que el de los campos regulares, muy probablemente cerca del Map normal, pero eso requiere más pruebas.

Para el soporte de propiedades anulables y la implementación segura de subprocesos, consulte here .


No puede agregar un campo, pero puede agregar una propiedad, que delega a otras propiedades / métodos del objeto para implementar su (s) accesor (es). Por ejemplo, suponga que desea agregar una propiedad secondsSinceEpoch a la clase java.util.Date , puede escribir

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


No, la documentation explica esto:

Las extensiones no modifican realmente las clases que extienden. Al definir una extensión, no inserta nuevos miembros en una clase, sino que simplemente hace que las nuevas funciones se puedan llamar con la notación de puntos en las instancias de esta clase.

y

Tenga en cuenta que, dado que las extensiones en realidad no insertan miembros en las clases, no hay una forma eficiente de que una propiedad de extensión tenga un campo de respaldo. Es por esto que los inicializadores no están permitidos para las propiedades de extensión. Su comportamiento solo se puede definir proporcionando explícitamente a los getters / setters.

Pensar en las funciones / propiedades de extensión como simplemente azúcar sintáctica para llamar a una función estática y pasar un valor con suerte lo deja claro.


Puede crear una propiedad de extensión con getter y setter anulados:

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

Pero no puede crear una propiedad de extensión con un campo de respaldo porque no puede agregar un campo a una clase existente.