声明站点差异可能导致ClassCastException

时间:2017-07-28 15:34:39

标签: kotlin

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

在关键字中使用/关键字的方法是推荐做法吗?为什么?

1 个答案:

答案 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 projectionsobjects强制转换为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 ;参数。