Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does 'by' keyword do in Kotlin?

Tags:

kotlin

In simple words, you can understand by keyword as provided by.

From the perspective of property consumer, val is something that has getter (get) and var is something that has getter and setter (get, set). For each var property there is a default provider of get and set methods that we don't need to specify explicitly.

But, when using by keyword, you are stating that this getter/getter&setter is provided elsewhere (i.e. it's been delegated). It's provided by the function that comes after by.

So, instead of using this built-in get and set methods, you are delegating that job to some explicit function.

One very common example is the by lazy for lazy loading properties. Also, if you are using dependency injection library like Koin, you'll see many properties defined like this:

var myRepository: MyRepository by inject()  //inject is a function from Koin

In the class definition, it follows the same principle, it defines where some function is provided, but it can refer to any set of methods/properties, not just get and set.

class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface

This code is saying: 'I am class MyClass and I offer functions of interface SomeInterface which are provided by SomeImplementation. I'll implement SomeOtherInterface by myself (that's implicit, so no by there).'


In the Kotlin reference you will find two uses for by, the first being Delegated Properties which is the use you have above:

There are certain common kinds of properties, that, though we can implement them manually every time we need them, would be very nice to implement once and for all, and put into a library. Examples include lazy properties: the value gets computed only upon first access, observable properties: listeners get notified about changes to this property, storing properties in a map, not in separate field each.

Here you delegate the getter/setter to another class that does the work and can contain common code. As another example, some of the dependency injectors for Kotlin support this model by delegating the getter to receiving a value from a registry of instances managed by the dependency injection engine.

And Interface/Class delegation is the other use:

The Delegation pattern has proven to be a good alternative to implementation inheritance, and Kotlin supports it natively requiring zero boilerplate code. A class Derived can inherit from an interface Base and delegate all of its public methods to a specified object

Here you can delegate an interface to another implementation so the implementing class only needs to override what it wants to change, while the rest of the methods delegate back to a fuller implementation.

A live example would be the Klutter Readonly/Immutable collections where they really just delegate the specific collection interface to another class and then override anything that needs to be different in the readonly implementation. Saving a lot of work not having to manually delegate all of the other methods.

Both of these are covered by the Kotlin language reference, start there for base topics of the language.


enter image description here

The syntax is:

val/var <property name>: <Type> by <expression>. 

The expression after by is the delegate

if we try to access the value of property p, in other words, if we call get() method of property p, the getValue() method of Delegate instance is invoked.

If we try to set the value of property p, in other words, if we call set() method of property p, the setValue() method of Delegate instance is invoked.


Delegation for property:

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

Result:

textA
value = textB

Delegation for class:

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

Result:

property from ClassA
fun from ClassA

Delegation for parameters:

// 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}")
    }
}

Result:

name: John; age: 30; address: city, street; id: 5000