我有一个kotlin对象定义如下:
data class UserUpdateRequest(val map: Map<String, Any?>) {
@get:Email
val email: String? by map
val firstName: String? by map
val lastName: String? by map
}
这样工作得很好,所以我遇到的问题是属性可以为空,当我访问一个时,比如说instance.email
它会抛出一个NoSuchElementException
如果所说的属性不是在地图中设置。
相反,它返回null
会更方便,因为它是可选的/可空的。有没有办法在不编写我自己的代表的情况下实现这一目标?
答案 0 :(得分:10)
您基本上可以使用.withDefault { ... }
扩展包装Map
进行委派,以便它执行lambda来计算缺席密钥的值:
data class UserUpdateRequest(val map: Map<String, Any?>) {
private val defaultMap = map.withDefault { null }
@get:Email
val email: String? by defaultMap
val firstName: String? by defaultMap
val lastName: String? by defaultMap
}
请注意,此包装器不处理简单的defaultMap.get(key)
和defaultMap[key]
查询,它只影响defaulMap.getValue(key)
调用(委托实现也恰好使用它)。
答案 1 :(得分:1)
如果您想更好地控制委托的工作并了解其工作原理,那么这里是委托的实现。
data class UserUpdateRequest(val map: Map<String, Any?>) {
var email: String? by valueFromMap(map = map)
var firstName: String? by valueFromMap(map = map)
var lastName: String? by valueFromMap(map = map)
var age: Int? by valueFromMap(map = map)
}
val myInitialMap = mutableMapOf<String,Any?>()
myInitialMap["email"] = "1234@something.com"
myInitialMap["age"] = 34
val request = UserUpdateRequest(myInitialMap)
request.lastName = "Genericname"
println("email: ${request.email}, firstNme: ${request.firstName}, lastName: ${request.lastName}, age: ${request.age}")
request.lastName = null
println("lastName after setting to null: ${request.lastName}")
这将打印:
电子邮件:1234@something.com,名字:null,姓氏:通用名称,年龄:34
设置为null后的lastName:null
因此,如果变量存在且其类型与变量类型匹配,则将其设置为initialMap的值。否则将变量设置为null。
fun <R>valueFromMap(key: (KProperty<*>) -> String = KProperty<*>::name,
map: Map<String, Any?>): ReadWriteProperty<Any, R?>{
val myMap = map.toMutableMap()
return object : ReadWriteProperty<Any, R?> {
override fun getValue(thisRef: Any, property: KProperty<*>): R? {
return (myMap.getOrDefault(key(property), null) as? R)?: return null
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: R?) {
if(value == null){
myMap.remove(key(property))
} else {
myMap[key(property)] = value
}
}
}
}
因此valueFromMap(map = map)是一个为val / var(KProperty)返回ReadWriteProperty的函数,该函数使用KPproperty的名称作为键在地图中查找作为参数传递的值。
因此,当我们致电:
request.lastName = "Genericname"
在上面的示例中,实际上我们调用。
initialMap["lastName"] = "Genericname"
如果变量名称和相应的键不同,则您甚至可以将键作为参数传递。
使用这种方法,您可以通过getValue和setValue函数自行处理值访问。
希望这对任何人都有帮助。