文章

多种类型的键值对设计

多种类型的键值对设计

从源码看多种类型的键值对设计

ViewModel CreationExtras Key 设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public sealed class CreationExtras {
    internal val map: MutableMap<Key<*>, Any?> = mutableMapOf()
    /**
     * Key for the elements of [CreationExtras]. [T] is a type of an element with this key.
     */
    public interface Key<T>
    /**
     * Returns an element associated with the given [key]
     */
    public abstract operator fun <T> get(key: Key<T>): T?
    object Empty : CreationExtras() {
        override fun <T> get(key: Key<T>): T? = null
    }
}

Key 的泛型 T 代表对应 Value 的类型。相对于 Map<K,V> ,这种定义方式可以更加类型安全地获取多种类型的键值对,CoroutineContext 等也是采用这种设计。

CoroutineContext

多数据类型的键值对设计

一般

一个 MutableMap<K, Any?>,要存储多种数据类型,Value 设计成 Any?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
val map: MutableMap<String, Any?> = mutableMapOf()  
// 存储不同类型的值  
map["key1"] = 1  
map["key2"] = "hacket"  
map["key3"] = true  
// 取出来,需要强制转换  
val v1 = map["key1"] as Int  
val v2 = map["key2"] as String  
val v3 = map["key3"] as Boolean

// 或者
class NormalKvCache {  
    val map: MutableMap<String, Any?> = mutableMapOf()  
    fun put(key: String, v: Any?) {  
        map[key] = v  
    }  
    fun <T> get(key: String): T? {  
        return map[key] as T?  
    }  
}
// 测试
val normalKvCache = NormalKvCache()  
normalKvCache.put("k1", "hacket");  
normalKvCache.put("k2", 2)  
normalKvCache.put("k3", true)  
  
val v1 = normalKvCache.get<String>("k1")  
val v2 = normalKvCache.get<Int>("k2")  
val v3 = normalKvCache.get<Boolean>("k3")

这种方式,取出来的值需要手动转换成存进去的类型,不够安全。

基于 Key<T> 设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MyKvCache {  
    internal val map: MutableMap<Key<*>, Any?> = mutableMapOf()  
  
    interface Key<T>  
  
    /**  
     * Associates the given [key] with [t]  
     */  
    operator fun <T> set(key: Key<T>, t: T) {  
        map[key] = t  
    }  
  
    fun <T> get(key: Key<T>): T? {  
        @Suppress("UNCHECKED_CAST")  
        return map[key] as T?  
    }  
  
    companion object {  
        private object K1 : Key<String>  
  
        @JvmField  
        val KEY1: Key<String> = K1  
  
        private object K2 : Key<String>  
  
        @JvmField  
        val KEY2: Key<String> = K2  
  
        private object K3 : Key<Int>  
  
        @JvmField  
        val KEY3: Key<Int> = K3  
  
        private object K4 : Key<Boolean>  
  
        @JvmField  
        val KEY4: Key<Boolean> = K4  
    }  
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
fun test1() {  
    val myKvCache = MyKvCache()  
    myKvCache[MyKvCache.KEY1] = "hacket1"  
    myKvCache[MyKvCache.KEY2] = "hacket2"  
    myKvCache[MyKvCache.KEY3] = 11  
    myKvCache[MyKvCache.KEY4] = true  
  
    val v1 = myKvCache.get(MyKvCache.KEY1)  
    val v2 = myKvCache.get(MyKvCache.KEY2)  
    val v3 = myKvCache.get(MyKvCache.KEY3)  
    val v4 = myKvCache.get(MyKvCache.KEY4)  
    println("v1=$v1, v2=$v2, v3=$v3, v4=$v4")  
}
本文由作者按照 CC BY 4.0 进行授权