
Map, which has keys of type String. When we use it as a delegate and we ask for a property value, the result is the value associated with this property name.fun main() { val map: Map<String, Any> = mapOf( "name" to "Marcin", "kotlinProgrammer" to true ) val name: String by map val kotlinProgrammer: Boolean by map println(name) // Marcin println(kotlinProgrammer) // true }
Map be a delegate? To be able to use an object as a read-only property delegate, this object must have a getValue function. For Map, it is defined as an extension function in Kotlin stdlib.operator fun <V, V1 : V> Map<String, V>.getValue( thisRef: Any?, property: KProperty<*> ): V1 { val key = property.name val value = get(key) if (value == null && !containsKey(key)) { throw NoSuchElementException( "Key ${property.name} is missing in the map." ) } else { return value as V1 } }
Map as a delegate? In most applications, you should not need it. However, you might be forced by an API to treat objects as maps that have some expected keys and some that might be added dynamically in the future. I mean situations like "This endpoint will return an object representing a user, with properties id, displayName, etc., and on the profile page you need to iterate over all these properties, including those that are not known in advance, and display an appropriate view for each of them". For such a requirement, we need to represent an object using Map, then we can use this map as a delegate in order to more easily use properties we know we can expect.class User(val map: Map<String, Any>) { val id: Long by map val name: String by map } fun main() { val user = User( mapOf<String, Any>( "id" to 1234L, "name" to "Marcin" ) ) println(user.name) // Marcin println(user.id) // 1234 println(user.map) // {id=1234, name=Marcin} }
Map can only be used for read-only properties because it is a read-only interface. For read-write properties, use MutableMap. Any delegated property change is a change in the map it delegates to, and any change in this map leads to a different property value. Delegating to a MutableMap map is like accessing the shared state of a read/write data source.class User(val map: MutableMap<String, Any>) { var id: Long by map var name: String by map } fun main() { val user = User( mutableMapOf( "id" to 123L, "name" to "Alek", ) ) println(user.name) // Alek println(user.id) // 123 user.name = "Bolek" println(user.name) // Bolek println(user.map) // {id=123, name=Bolek} user.map["id"] = 456 println(user.id) // 456 println(user.map) // {id=456, name=Bolek} }
class Population(var cities: Map<String, Int>) { val sanFrancisco by cities val tallinn by cities val kotlin by cities } val population = Population( mapOf( "sanFrancisco" to 864_816, "tallinn" to 413_782, "kotlin" to 43_005 ) ) fun main(args: Array<String>) { // Years has passed, // now we all live on Mars population.cities = emptyMap() println(population.sanFrancisco) println(population.tallinn) println(population.kotlin) }
fun main() { var a = 10 var b = a a = 20 println(b) }
10 because variables are always assigned to values, never other variables. First, a is assigned to 10, then b is assigned to 10, then a changes and is assigned to 20. This does not change the fact that b is assigned to 10.
fun main() { val user1 = object { var name: String = "Rafał" } val user2 = user1 user1.name = "Bartek" println(user2.name) }
user1 and user2 reference the same object, and then this object changes internally.
user1 references instead of changing the value of name.interface Nameable { val name: String } fun main() { var user1: Namable = object : Nameable { override var name: String = "Rafał" } val user2 = user1 user1 = object : Nameable { override var name: String = "Bartek" } println(user2.name) }

var, especially since both can be changed with the += sign. First, take a look at the following snippet:fun main() { var list1 = listOf(1, 2, 3) var list2 = list1 list1 += 4 println(list2) }
[1, 2, 3]. It has to be this way because list1 references a read-only list. This means that list1 += 4 in this case means list1 = list1 + 4, and so a new list object is returned. Now, let's take a look at the following snippet:fun main() { val list1 = mutableListOf(1, 2, 3) val list2 = list1 list1 += 4 println(list2) }
[1, 2, 3, 4] because list1 references a mutable list. This means that list1 += 4 in this case means list1.plusAssign(4), i.e., list1.add(4), and the same list object is returned. Now, consider using Map as a delegate:fun main() { var map = mapOf("a" to 10) val a by map map = mapOf("a" to 20) println(a) }
10? On the other hand, if the map were mutable, the answer would be different:fun main() { val mmap = mutableMapOf("a" to 10) val a by mmap mmap["a"] = 20 println(a) }
20? This is consistent with the behavior of the other variables and with what properties are compiled to.var map = mapOf("a" to 10) // val a by map // is compiled to val `a$delegate` = map val a: Int get() = `a$delegate`.getValue(null, ::a) val mmap = mutableMapOf("b" to 10) // val b by mmap // is compiled to val `b$delegate` = mmap val b: Int get() = `b$delegate`.getValue(null, ::b) fun main() { map = mapOf("a" to 20) println(a) // 10 mmap["b"] = 20 println(b) // 20 }
cities property should not influence the value of sanFrancisco, tallinn, or kotlin. In Kotlin, we delegate to a delegate, not to a property, just like we assign a property to a value, not another property.class Population(var cities: Map<String, Int>) { val sanFrancisco by cities val tallinn by cities val kotlin by cities } val population = Population( mapOf( "sanFrancisco" to 864_816, "tallinn" to 413_782, "kotlin" to 43_005 ) ) fun main(args: Array<String>) { // Years has passed, // now we all live on Mars population.cities = emptyMap() println(population.sanFrancisco) println(population.tallinn) println(population.kotlin) }
cities map would have to be mutable, and using population.cities.clear() would cause population.sanFrancisco to fail.Delegates.notNull, lazy, Delegates.observable, Delegates.vetoable, Map<String, T> and MutableMap<String, T>.I hope you will find this knowledge useful in your programming practice.
