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

Posted

技术标签:

【中文标题】Kotlin:如何使用列表强制转换:未经检查的强制转换:kotlin.collections.List<Kotlin.Any?> 到 kotlin.colletions.List<Waypoint>【英文标题】:Kotlin: How to work with List casts: Unchecked Cast: kotlin.collections.List<Kotlin.Any?> to 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

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

【问题讨论】:

【参考方案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

【讨论】:

以上是关于Kotlin:如何使用列表强制转换:未经检查的强制转换:kotlin.collections.List<Kotlin.Any?> 到 kotlin.colletions.List<W的主要内容,如果未能解决你的问题,请参考以下文章

类型安全:从 Object 到 List<MyObject> 的未经检查的强制转换

对象到 ArrayList<String> 未经检查的强制转换

Servlet ArrayList 到 JSP(从 Object 到 ArrayList 的未经检查的强制转换)

未经检查的强制转换:'java.io.Serializable'为'java.util.ArrayList “

null 不能强制转换为非 null 类型 kotlin.String NullPointer 异常

如何使用警卫同时解开 Swift 可选和强制类型转换?