Dea*_* Xu 7 delegates jvm generic-variance kotlin
我是Kotlin的新手.当我在地图中学习存储属性时.我尝试以下用法.
class User(val map: MutableMap<String, String>) {
val name: String by map
}
Run Code Online (Sandbox Code Playgroud)
class User(val map: MutableMap<String, in String>) {
val name: String by map
}
Run Code Online (Sandbox Code Playgroud)
class User(val map: MutableMap<String, out String>) {
val name: String by map
}
Run Code Online (Sandbox Code Playgroud)
前两个都是工作,最后一个都失败了.使用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
Run Code Online (Sandbox Code Playgroud)
我们可以看到,它会导致一个NullPointerException.
为什么地图代表不允许使用逆变?
为什么kotlin不会给我编译错误?
是的...编译器在这里肯定是错误的。(使用 Kotlin 版本 1.1.2-5 进行测试)
首先,在将属性委托给映射的情况下,您可以使用属性的名称在映射中查找它的值。
使用MutableMap<String, in String>, 相当于 Java 中Map<String, ? super String>使用逆变的。
使用MutableMap<String, out String>, 相当于 JavaMap<String, ? extends String>使用协方差。
(你把两者搞混了)
协变类型可以用作生产者。逆变类型可以用作消费者。(请参阅PECS。抱歉,我没有 Kotlin 特定的链接,但原则仍然适用)。
按映射委托使用映射的第二个通用类型作为生产者(您从映射中获取内容),因此不可能使用 a,MutableMap<String, in String>因为它的第二个参数是消费者(将内容放入其中)。
MutableMap<String, out String>由于某种原因,编译器在 a 的情况下生成 a 所需的代码MutableMap<String, in String>,这是错误的,如本例所示:
class User(val map: MutableMap<String, in String>) {
val name: String by map
}
fun main(args:Array<String>){
val m: MutableMap<String, CharSequence> = mutableMapOf("name" to StringBuilder())
val a = User(m)
val s: String = a.name
}
Run Code Online (Sandbox Code Playgroud)
您将得到一个类转换异常,因为虚拟机正在尝试将 aStringBuilder视为String. 但您不使用任何显式强制转换,因此它应该是安全的。
throw null不幸的是,它在 的有效用例中生成垃圾 ( ) out。
在使用协方差()的情况下String并没有真正意义out,因为String它是最终的,但在不同类型层次结构的情况下,我能想到的唯一解决方法是手动修补字节码,这是一场噩梦。
我不知道是否有现有的错误报告。我想我们只能等到这个问题得到解决。
| 归档时间: |
|
| 查看次数: |
357 次 |
| 最近记录: |