Mientras desarrollo para Android, a veces me encuentro con algo que se parece a esto:
var someModel: someViewModel by notNullAndObservable { vm ->
...
}
No entiendo cuál es el significado de la by
palabra clave.
Respuestas:
En la referencia de Kotlin encontrará dos usos by
, el primero es Propiedades delegadas, que es el uso que tiene anteriormente:
Hay ciertos tipos comunes de propiedades que, aunque podemos implementarlas manualmente cada vez que las necesitemos, sería muy bueno implementarlas de una vez por todas y ponerlas en una biblioteca. Los ejemplos incluyen propiedades diferidas: el valor se calcula solo en el primer acceso, propiedades observables: los oyentes reciben notificaciones sobre los cambios en esta propiedad, almacenando propiedades en un mapa, no en un campo separado cada una.
Aquí delegas el getter / setter a otra clase que hace el trabajo y puede contener código común. Como otro ejemplo, algunos de los inyectores de dependencia para Kotlin admiten este modelo al delegar el captador para recibir un valor de un registro de instancias administrado por el motor de inyección de dependencia.
Y la delegación de interfaz / clase es el otro uso:
El patrón de delegación ha demostrado ser una buena alternativa a la herencia de implementación, y Kotlin lo admite de forma nativa y no requiere código repetitivo. Una clase Derived puede heredar de una interfaz Base y delegar todos sus métodos públicos a un objeto específico
Aquí puede delegar una interfaz a otra implementación, por lo que la clase de implementación solo necesita anular lo que quiere cambiar, mientras que el resto de los métodos delegan nuevamente a una implementación más completa.
Un ejemplo vivo serían las colecciones de Klutter Readonly / Immutable donde realmente solo delegan la interfaz de colección específica a otra clase y luego anulan cualquier cosa que deba ser diferente en la implementación de solo lectura. Ahorro de mucho trabajo al no tener que delegar manualmente todos los demás métodos.
Ambos están cubiertos por la referencia del idioma Kotlin , comience allí para los temas básicos del idioma.
En palabras simples, puede comprender la by
palabra clave proporcionada por .
Desde la perspectiva del consumidor de propiedades, val
es algo que tiene getter (get) y var
es algo que tiene getter y setter (get, set). Para cada var
propiedad hay un proveedor predeterminado de métodos get y set que no necesitamos especificar explícitamente.
Pero, al usar una by
palabra clave, está indicando que este getter / getter & setter se proporciona en otro lugar (es decir, se ha delegado). Está provisto por la función que viene después by
.
Entonces, en lugar de usar estos métodos incorporados de obtención y configuración, está delegando ese trabajo a alguna función explícita.
Un ejemplo muy común es el by lazy
de las propiedades de carga diferida. Además, si está utilizando una biblioteca de inyección de dependencias como Koin, verá muchas propiedades definidas así:
var myRepository: MyRepository by inject() //inject is a function from Koin
En la definición de clase, sigue el mismo principio, define dónde se proporciona alguna función, pero puede referirse a cualquier conjunto de métodos / propiedades, no solo a obtener y establecer.
class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface
Este código dice: 'Soy clase MyClass y ofrezco funciones de interfaz SomeInterface que son proporcionadas por SomeImplementation. Implementaré SomeOtherInterface por mí mismo (eso está implícito, así que no by
) '.
La sintaxis es:
val/var <property name>: <Type> by <expression>.
La expresión después de es el delegado
si intentamos acceder al valor de la propiedad p , en otras palabras, si llamamos al método get () de la propiedad p , se invoca el método getValue () de la instancia Delegate .
Si intentamos establecer el valor de la propiedad p , en otras palabras, si llamamos al método set () de la propiedad p , se invoca el método setValue () de la instancia Delegate .
Delegación de propiedad:
import kotlin.reflect.KProperty
class Delegate {
// for get() method, ref - a reference to the object from
// which property is read. prop - property
operator fun getValue(ref: Any?, prop: KProperty<*>) = "textA"
// for set() method, 'v' stores the assigned value
operator fun setValue(ref: Any?, prop: KProperty<*>, v: String) {
println("value = $v")
}
}
object SampleBy {
var s: String by Delegate() // delegation for property
@JvmStatic fun main(args: Array<String>) {
println(s)
s = "textB"
}
}
Resultado:
textA
value = textB
Delegación de clase:
interface BaseInterface {
val value: String
fun f()
}
class ClassA: BaseInterface {
override val value = "property from ClassA"
override fun f() { println("fun from ClassA") }
}
// The ClassB can implement the BaseInterface by delegating all public
// members from the ClassA.
class ClassB(classA: BaseInterface): BaseInterface by classA {}
object SampleBy {
@JvmStatic fun main(args: Array<String>) {
val classB = ClassB(ClassA())
println(classB.value)
classB.f()
}
}
Resultado:
property from ClassA
fun from ClassA
Delegación de parámetros:
// for val properties Map is used; for var MutableMap is used
class User(mapA: Map<String, Any?>, mapB: MutableMap<String, Any?>) {
val name: String by mapA
val age: Int by mapA
var address: String by mapB
var id: Long by mapB
}
object SampleBy {
@JvmStatic fun main(args: Array<String>) {
val user = User(mapOf("name" to "John", "age" to 30),
mutableMapOf("address" to "city, street", "id" to 5000L))
println("name: ${user.name}; age: ${user.age}; " +
"address: ${user.address}; id: ${user.id}")
}
}
Resultado:
name: John; age: 30; address: city, street; id: 5000