【问题标题】:Kotlin: How to work with List casts: Unchecked Cast: kotlin.collections.List<Kotlin.Any?> to kotlin.colletions.List<Waypoint>Kotlin:如何使用列表强制转换:未经检查的强制转换:kotlin.collections.List<Kotlin.Any?> 到 kotlin.colletions.List<Waypoint>
【发布时间】:2016-04-12 09:41:26
【问题描述】:

我想编写一个函数,它返回List 中不是第一个或最后一个项目(通过点)的每个项目。该函数获取一个通用的List&lt;*&gt; 作为输入。仅当列表的元素为 Waypoint 类型时才应返回结果:

fun getViaPoints(list: List<*>): List<Waypoint>? {

    list.forEach { if(it !is Waypoint ) return null }

    val waypointList = list as? List<Waypoint> ?: return null

    return waypointList.filter{ waypointList.indexOf(it) != 0 && waypointList.indexOf(it) != waypointList.lastIndex}
}

List&lt;*&gt; 转换为List&lt;Waypoint&gt; 时,我收到警告:

未经检查的演员表:kotlin.collections.List 到 kotlin.colletions.List

我想不出其他方法来实现它。在没有此警告的情况下实现此功能的正确方法是什么?

【问题讨论】:

    标签: list generics casting kotlin


    【解决方案1】:

    在 Kotlin 中,在一般情况下无法在运行时检查泛型参数(例如仅检查 List&lt;T&gt; 的项目,这只是一种特殊情况),因此将泛型类型转换为具有不同泛型参数的另一个除非演员表位于variance bounds 内,否则将发出警告。

    但是有不同的解决方案:

    • 你已经检查了类型并且你很确定这个演员是安全的。鉴于此,您可以使用suppress the warning@Suppress("UNCHECKED_CAST")

      @Suppress("UNCHECKED_CAST")
      val waypointList = list as? List<Waypoint> ?: return null
      
    • 使用.filterIsInstance&lt;T&gt;()函数,它检查项目类型并返回一个包含所传递类型项目的列表:

      val waypointList: List<Waypoint> = list.filterIsInstance<Waypoint>()
      
      if (waypointList.size != list.size)
          return null
      

      或在一个语句中相同:

      val waypointList = list.filterIsInstance<Waypoint>()
          .apply { if (size != list.size) return null }
      

      这将创建一个所需类型的新列表(从而避免内部未经检查的强制转换),引入一点开销,但同时它使您免于遍历 list 并检查类型(在 list.foreach { ... }行),所以不会很明显。

    • 编写一个实用函数来检查类型并在类型正确时返回相同的列表,从而将强制转换(从编译器的角度来看仍然未经检查)封装在其中:

      @Suppress("UNCHECKED_CAST")
      inline fun <reified T : Any> List<*>.checkItemsAre() =
              if (all { it is T })
                  this as List<T>
              else null
      

      配合用法:

      val waypointList = list.checkItemsAre<Waypoint>() ?: return null
      

    【讨论】:

    • 很好的答案!我选择 list.filterIsInstance() 解决方案,因为我认为它是最干净的解决方案。
    • 请注意,如果您使用filterIsInstance 并且原始列表包含不同类型的元素,您的代码将默默地过滤掉它们。有时这是您想要的,但有时您可能宁愿抛出IllegalStateException 或类似的东西。如果是后者,那么您可以创建自己的方法来检查然后强制转换:inline fun &lt;reified R&gt; Iterable&lt;*&gt;.mapAsInstance() = map { it.apply { check(this is R) } as R }
    • 注意.apply不返回lambda的返回值,它返回receive对象。如果您希望选项返回 null,您可能需要使用 .takeIf
    【解决方案2】:

    为了改进@hotkey 的答案,这是我的解决方案:

    val waypointList = list.filterIsInstance<Waypoint>().takeIf { it.size == list.size }
    

    如果所有项目都可以转换,则为您提供List&lt;Waypoint&gt;,否则为 null。

    【讨论】:

      【解决方案3】:

      在泛型类的情况下,由于类型信息在运行时被删除,因此无法检查强制转换。但是您检查列表中的所有对象是否为Waypoints,因此您可以使用@Suppress("UNCHECKED_CAST") 取消警告。

      为避免此类警告,您必须传递可转换为 Waypoint 的对象的 List。当您使用 * 但尝试将此列表作为类型化列表访问时,您将始终需要强制转换,并且该强制转换不会被选中。

      【讨论】:

        【解决方案4】:

        当用于检查可序列化到列表对象时,我对@hotkey 答案做了一些改动:

            @Suppress("UNCHECKED_CAST")
            inline fun <reified T : Any> Serializable.checkSerializableIsListOf() =
                if (this is List<*> && this.all { it is T })
                  this as List<T>
                else null
        

        【讨论】:

        • 正在寻找这样的解决方案,但出现此错误:Cannot access 'Serializable': it is internal in 'kotlin.io'
        • 解决方案:在android中传递一个用户定义的对象,你的类应该实现Parcelable而不是Serializable接口。
        【解决方案5】:

        代替

        myGenericList.filter { it is AbstractRobotTurn } as List<AbstractRobotTurn>
        

        我喜欢做

        myGenericList.filter { it is AbstractRobotTurn }.map { it as AbstractRobotTurn }
        

        不确定性能如何,但至少没有警告。

        【讨论】:

          【解决方案6】:

          Kotlin 在编译时确保涉及泛型的操作的类型安全,而在运行时,泛型类型的实例不保存有关其实际类型参数的信息。例如,List 被擦除为 List。 一般情况下,无法在运行时检查实例是否属于具有特定类型参数的泛型类型。

          https://kotlinlang.org/docs/typecasts.html#type-erasure-and-generic-type-checks

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-10-13
            • 1970-01-01
            • 2017-06-04
            • 2010-10-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多