Kotlin: 如何使用 List 强制类型转换: Uncheck Cast: Kotlin.Collection. List < Kotlin. Any? > to Kotlin.coltions. List < Waypoint >

我想编写一个函数,返回 List中不是第一个或最后一个项目(通过点)的每个项目。该函数获取一个通用 List<*>作为输入。只有当列表的元素类型为 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<*>转换为 List<Waypoint>时,我会收到警告:

未检查的 Cast: kotlin.Collection. List Kotlin 收藏,名单

否则我想不出办法来实现它。在没有这个警告的情况下实现这个函数的正确方法是什么?

90859 次浏览

在泛型类强制转换无法检查的情况下,因为类型信息在运行时被擦除。但是您可以检查列表中的所有对象是否都是 Waypoint,因此可以使用 @Suppress("UNCHECKED_CAST")禁止显示警告。

为了避免这样的警告,您必须传递可转换为 Waypoint的对象的 List。当您使用 *但是试图以类型化列表的形式访问这个列表时,您总是需要一个强制转换,并且这个强制转换将不会被选中。

在 Kotlin,一般情况下没有办法在运行时检查泛型参数(就像只检查 List<T>的项目,这只是一个特例) ,所以将泛型类型强制转换为另一个具有不同泛型参数的类型将会引起警告,除非强制转换位于 方差界内。

然而,有不同的解决方案:

  • 您已经检查了类型,并且您非常确信模型是安全的。既然如此,你可以用 @Suppress("UNCHECKED_CAST")压制警告

    @Suppress("UNCHECKED_CAST")
    val waypointList = list as? List<Waypoint> ?: return null
    
  • Use .filterIsInstance<T>() function, which checks the item types and returns a list with the items of the passed type:

    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
    

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

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

如果所有项目都可以强制转换,则返回 List<Waypoint>,否则返回 null。

当我用@hotkey 来检查 Serializable to List 对象时,我做了一个小小的变化:

    @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

而不是

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

我喜欢做

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

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

Kotlin 确保编译时涉及泛型的操作的类型安全,而在运行时,泛型类型的实例不包含关于其实际类型参数的信息。例如,List 被擦除为仅仅是 List < * > 。通常,在运行时无法检查实例是否属于具有某些类型参数的泛型类型。

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