Kotlin:如何使用列表类型转换:未经检查的类型转换:kotlin.collections.List<Kotlin.Any?> 到 kotlin.colletions.List<Waypoint>

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36569421/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 08:10:29  来源:igfitidea点击:

Kotlin: How to work with List casts: Unchecked Cast: kotlin.collections.List<Kotlin.Any?> to kotlin.colletions.List<Waypoint>

listgenericscastingkotlin

提问by Lukas Lechner

I want to write a function that returns every item in a Listthat is not the first or the last item (a via point). The function gets a generic List<*>as input. A result should only be returned if the elements of the list are of the type Waypoint:

我想编写一个函数,该函数返回 aList中不是第一个或最后一个项目(通过点)的每个项目。该函数获取泛型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}
}

When casting the List<*>to List<Waypoint>, I get the warning:

投射List<*>to 时List<Waypoint>,我收到警告:

Unchecked Cast: kotlin.collections.List to kotlin.colletions.List

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

I can't figure out a way to implement it otherwise. What's the right way to implement this function without this warning?

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

回答by hotkey

In Kotlin, there's no way to check the generic parameters at runtime in general case (like just checking the items of a List<T>, which is only a special case), so casting a generic type to another with different generic parameters will raise a warning unless the cast lies within variance bounds.

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

There are different solutions, however:

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

  • You have checked the type and you are quite sure that the cast is safe. Given that, you can suppress the warningwith @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
    

    or the same in one statement:

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

    This will create a new list of the desired type (thus avoiding unchecked cast inside), introducing a little overhead, but in the same time it saves you from iterating through the listand checking the types (in list.foreach { ... }line), so it won't be noticeable.

  • Write a utility function that checks the type and returns the same list if the type is correct, thus encapsulating the cast (still unchecked from the compiler's point of view) inside it:

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

    With the usage:

    val waypointList = list.checkItemsAre<Waypoint>() ?: return null
    
  • 您已经检查了类型并且您非常确定演员表是安全的。鉴于这种情况,你可以取消此警告@Suppress("UNCHECKED_CAST")

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

    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
    

回答by Adam Kis

To improve @hotkey's answer here's my solution:

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

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

This gives you the List<Waypoint>if all the items can be casted, null otherwise.

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

回答by Michael

In case of generic classes casts cannot be checked because type information is erased in runtime. But you check that all objects in the list are Waypoints so you can just suppress the warning with @Suppress("UNCHECKED_CAST").

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

To avoid such warnings you have to pass a Listof objects convertible to Waypoint. When you're using *but trying to access this list as a typed list you'll always need a cast and this cast will be unchecked.

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

回答by Samiami Jankis

I made a little variation to @hotkey answer when used to check Serializable to List objects :

当用于检查 Serializable to List objects 时,我对@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

回答by Ludvig Linse

Instead of

代替

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

I like doing

我喜欢做

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

Not sure how performant this is, but no warnings at least.

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