¿Qué hace la palabra clave 'por' en Kotlin?

Respuestas:

74

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.

Jayson Minard
fuente
83

En palabras simples, puede comprender la bypalabra clave proporcionada por .

Desde la perspectiva del consumidor de propiedades, vales algo que tiene getter (get) y vares algo que tiene getter y setter (get, set). Para cada varpropiedad hay un proveedor predeterminado de métodos get y set que no necesitamos especificar explícitamente.

Pero, al usar una bypalabra 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 lazyde 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) '.

daneejela
fuente
18

ingrese la descripción de la imagen aquí

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 .

oiyio
fuente
5

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
alexrnov
fuente