- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我是 Kotlin 的新手。当我学习 Storing Properties in a Map .我尝试以下用法。
class User(val map: MutableMap<String, String>) {
val name: String by map
}
class User(val map: MutableMap<String, in String>) {
val name: String by map
}
class User(val map: MutableMap<String, out String>) {
val name: String by map
}
前两个都可以,最后一个失败。使用 out
修饰符,getName
的字节码如下:
public final java.lang.String getName();
0 aload_0 [this]
1 getfield kotl.User.name$delegate : java.util.Map [11]
4 astore_1
5 aload_0 [this]
6 astore_2
7 getstatic kotl.User.$$delegatedProperties : kotlin.reflect.KProperty[] [15]
10 iconst_0
11 aaload
12 astore_3
13 aload_1
14 aload_3
15 invokeinterface kotlin.reflect.KProperty.getName() : java.lang.String [19] [nargs: 1]
20 invokestatic kotlin.collections.MapsKt.getOrImplicitDefaultNullable(java.util.Map, java.lang.Object) : java.lang.Object [25]
23 checkcast java.lang.Object [4]
26 aconst_null
27 athrow
Local variable table:
[pc: 0, pc: 28] local: this index: 0 type: kotl.User
如我们所见,它会导致NullPointerException
。
为什么 map 委托(delegate)上不允许逆变?
为什么 kotlin 没有给我编译错误?
最佳答案
简短回答:这不是编译器中的错误,而是operator getValue()
的签名的不幸结果。为 MutableMap
声明.
长答案: delegating properties由于标准库中的以下三个运算符函数,to maps 是可能的:
// for delegating val to read-only map
operator fun <V, V1: V> Map<in String, @Exact V>.getValue(thisRef: Any?, property: KProperty<*>): V1
// for delegating var to mutable map
operator fun <V> MutableMap<in String, in V>.getValue(thisRef: Any?, property: KProperty<*>): V
operator fun <V> MutableMap<in String, in V>.setValue(thisRef: Any?, property: KProperty<*>, value: V)
这里是 MutableMap
的使用地点差异选择接收者以便可以将某种类型的属性委托(delegate)给可以存储其父类(super class)型的映射:
class Sample(val map: MutableMap<String, Any>) {
var stringValue: String by map
var intValue: Int by map
}
不幸的是,当您尝试使用超出投影的 MutableMap<String, out String>
作为 val
的委托(delegate)属性(property),因此作为 getValue
的接收者运算符(operator),这里会发生什么:
MutableMap<in String, in V>.getValue
选择了重载,因为它具有更具体的接收器类型。out String
类型参数投影它的实际类型参数是未知的(它可以是 MutableMap<..., String>
或 MutableMap<..., SubTypeOfString>
),所以唯一安全的选择是假设它是 Nothing
,它是所有可能类型的子类型。V
已推断为 Nothing
,并且编译器会插入一个检查,确认实际返回的值是 Nothing
类型。 ,这应该总是失败,因为不可能有 Nothing
类型的值.这个检查看起来像 throw null
在字节码中。我打开了一个问题 KT-18789看看我们可以用这个操作符函数的签名来做什么。
UPD:签名已在 Kotlin 1.2.20 中修复
同时作为一种解决方法,您可以使用 MutableMap
至Map
,因此 getValue
的第一个重载被选中:
class User(val map: MutableMap<String, out String>) {
val name: String by map as Map<String, String>
}
关于delegates - 为什么 kotlin 不允许协变 mutablemap 成为委托(delegate)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44665427/
在我的设置中,我试图有一个界面 Table继承自 Map (因为它主要用作 map 的包装器)。两个类继承自 Table - 本地和全局。全局的将有一个可变的映射,而本地的将有一个只有本地条目的映射。
Rust Nomicon 有 an entire section on variance除了关于 Box 的这一小节,我或多或少地理解了这一点和 Vec在 T 上(共同)变体. Box and Vec
我是一名优秀的程序员,十分优秀!