Kotlin介绍了here所述的声明网站差异。
在某些情况下,泛型参数的out / in关键字可能会导致ClassCastException。我的计划如下所示。
fun main(args: Array<String>) {
var l: List<String> = mutableListOf("string")
demo(l)
println("======")
for (s in l) {
println(s)
}
}
fun demo(strs: List<String>) {
val objects: List<Any> = strs // This is OK, since T is an out-parameter
if (objects is MutableList) {
val obs: MutableList<Any> = objects as MutableList<Any>
obs.add(TextView())
}
}
输出:
Exception in thread "main" java.lang.ClassCastException: com.kotlin.demo.clzz.TextView cannot be cast to java.lang.String
at com.kotlin.demo.clzz.Declaration_Site_VarianceKt.main(Declaration-Site-Variance.kt:14)
======
adn
在关键字中使用/关键字的方法是推荐做法吗?为什么?
答案 0 :(得分:1)
您的代码可以在没有任何警告的情况下编译,这是因为declaration-site variance仅在Kotlin中可用。
这与Java的使用站点差异形成对比,其中类型用法中的通配符使类型变为协变。
例如2个Soruce
接口在Kotlin中使用declaration-site variance:
interface Source<out T>
interface Source<in T>
两个Source
接口都将在Java中生成相同的源代码,如下所示:
// v---`T extends Object` rather than `? extends T`
public interface Source<T>{ /**/ }
这是因为通配符?
用作类型参数而不是Java中的类型参数。
T
中的Source<T>
是类型参数,? extends String
中的Source<? extends String>
是类型参数。
因此,如果您使用type projections将objects
强制转换为List<out Any>
,则编译器将报告 UNCHECKED_CAST 警告,例如:
fun demo(strs: List<String>) {
// v--- makes it explicitly by using out type proejction
val objects: List<out Any> = strs
if (objects is MutableList) {
// v--- an UNCHECKED_CAST warning reported
val obs: MutableList<Any> = objects as MutableList<Any>
obs.add(TextView())
}
}
换句话说,您无法将List<out Any>
分配给MutableList<Any>
。否则,您将收到编译错误。例如:
fun demo(strs: List<String>) {
val objects: List<out Any> = strs
if (objects is MutableList) {
// v--- ? extends Object
//ERROR: can't assign MutableList<out Any> to Mutable<Any>
// v ^--- Object
val obs: MutableList<Any> = objects
obs.add(TextView())
}
}
如果您将objects
分配给MutableList<out Any>
变量,您就会发现无法添加任何内容,因为您可以&#39;在Kotlin中创建Nothing
。例如:
fun demo(strs: List<String>) {
val objects: List<out Any> = strs
if (objects is MutableList) {
// v--- down-casting to `MutableList<out Any>`
val obs: MutableList<out Any> = objects
// v---ERROR: can't be instantiated
obs.add(Nothing())
}
}
问:是否建议使用out / in关键字?
Java描述了how to use a wildcard,它也适用于Kotlin。
&#34; &#34;变量,注意&#34; &#34;这里是? extends T
,与Kotlin out 方差相同:
&#34; in&#34;变量将数据提供给代码。想象一下带有两个参数的复制方法:
copy(src, dest)
。 src 参数提供了要复制的数据,因此它是&#34; in &#34;参数。
&#34; out &#34;变量,注意&#34; out &#34;这里是? super T
,与方差中的Kotlin 相同:
&#34; out &#34;变量保存数据供其他地方使用。在复制示例
copy(src, dest)
中, dest 参数接受数据,因此它是&#34; out &#34 ;参数。