Kotlin:如何处理列表转换:未选中Cast:kotlin.collections.List <Kotlin.Any?> to kotlin.colletions.List <Waypoint>

luk*_*kle 79 generics casting list kotlin

我想编写一个函数来返回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}
}
Run Code Online (Sandbox Code Playgroud)

当转换为List<*>to时List<Waypoint>,我收到警告:

未选中Cast:kotlin.collections.List to kotlin.colletions.List

我不知道如何实现它.在没有此警告的情况下实施此功能的正确方法是什么?

hot*_*key 148

在Kotlin中,一般情况下无法在运行时检查泛型参数(例如只检查a的项目List<T>,这只是一种特殊情况),因此将泛型类型转换为具有不同泛型参数的另一种类型将引发警告,除非cast位于方差范围内.

但是,有不同的解决方案:

  • 您已经检查了类型,并且您确信演员阵容是安全的.鉴于这种情况,你可以取消此警告@Suppress("UNCHECKED_CAST").

    @Suppress("UNCHECKED_CAST")
    val waypointList = list as? List<Waypoint> ?: return null
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用.filterIsInstance<T>()函数,它检查项类型并返回包含传递类型的项的列表:

    val waypointList: List<Waypoint> = list.filterIsInstance<Waypoint>()
    
    if (waypointList.size != list.size)
        return null
    
    Run Code Online (Sandbox Code Playgroud)

    或在同一声明中相同:

    val waypointList = list.filterIsInstance<Waypoint>()
        .apply { if (size != list.size) return null }
    
    Run Code Online (Sandbox Code Playgroud)

    这将创建一个所需类型的新列表(从而避免未经检查的内部转换),引入一点开销,但同时它可以避免迭代遍历list和检查类型(list.foreach { ... }在行),所以它不会显.

  • 编写一个检查类型的实用程序函数,如果类型正确则返回相同的列表,从而将转换(在编译器的视角中仍然未选中)封装在其中:

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

    用法:

    val waypointList = list.checkItemsAre<Waypoint>() ?: return null
    
    Run Code Online (Sandbox Code Playgroud)

  • 很棒的答案!我选择list.filterIsInstance <Waypoint>()解决方案,因为我认为这是最干净的解决方案. (4认同)
  • 请注意,如果您使用[`filterIsInstance`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter-is-instance.html)并且原始列表包含不同类型的元素,代码将默默过滤掉它们.有时候这就是你想要的,但有时候你可能会抛出一个`IllegalStateException`或类似的东西.如果是后一种情况,那么你可以创建自己的方法进行检查然后转换:`inline fun <reified R> Iterable <*>.mapAsInstance()= map {it.apply {check(this is R)} as R }` (4认同)
  • 请注意,.apply不返回lambda的返回值,而是返回接收对象。如果您希望该选项返回null,则可能要使用`.takeIf`。 (2认同)

Ada*_*Kis 15

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

val waypointList = list.filterIsInstance<Waypoint>().takeIf { it.size == list.size }
Run Code Online (Sandbox Code Playgroud)

这为您提供了List<Waypoint>是否可以投射所有项目,否则为 null。


Mic*_*ael 6

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

为避免此类警告,您必须将 a Listof objects 可转换为Waypoint. 当您正在使用*但试图将这个列表作为类型列表访问时,您将始终需要一个强制转换,并且这个强制转换将被取消选中。


小智 6

当用于检查可序列化到列表对象时,我对@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
Run Code Online (Sandbox Code Playgroud)