Kotlin:如何使用List演员表:未选中的Cast:kotlin.collections.List <Kotlin.Any?>到kotlin.colletions.List <Waypoint>


108

我想编写一个函数,该函数返回a中的每个项目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>,我得到的警告:

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

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

Answers:


190

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

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

  • 您已经检查了类型,并确定转换是安全的。鉴于这种情况,你可以取消此警告@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

6
好答案!我选择list.filterIsInstance <Waypoint>()解决方案,因为我认为这是最干净的解决方案。
卢卡斯·莱希纳

4
请注意,如果您使用filterIsInstance且原始列表包含不同类型的元素,则您的代码将以静默方式将其过滤掉。有时这是您想要的,但有时您可能希望IllegalStateException抛出一个或类似的结果。如果是后者,则您可以创建自己的方法进行检查然后进行inline fun <reified R> Iterable<*>.mapAsInstance() = map { it.apply { check(this is R) } as R }
强制转换

3
注意,.apply它不返回lambda的返回值,而是返回接收对象。.takeIf如果您希望该选项返回null,则可能要使用。
bj0

10

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

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

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


3

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

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


1

当用于检查可序列化到列表对象时,我对@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'
daviscodesbugs

0

代替

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

我喜欢做

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.