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<*>
作为输入。仅当列表的元素为 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
我想不出其他方法来实现它。在没有此警告的情况下实现此功能的正确方法是什么?
【问题讨论】:
【参考方案1】:在 Kotlin 中,在一般情况下无法在运行时检查泛型参数(例如仅检查 List<T>
的项目,这只是一种特殊情况),因此将泛型类型转换为具有不同泛型参数的另一个除非演员表位于variance bounds 内,否则将发出警告。
但是有不同的解决方案:
你已经检查了类型并且你很确定这个演员是安全的。鉴于此,您可以使用suppress the warning 和@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
【讨论】:
很好的答案!我选择 list.filterIsInstancefilterIsInstance
并且原始列表包含不同类型的元素,您的代码将默默地过滤掉它们。有时这是您想要的,但有时您可能宁愿抛出IllegalStateException
或类似的东西。如果是后者,那么您可以创建自己的方法来检查然后强制转换:inline fun <reified R> Iterable<*>.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<Waypoint>
,否则为 null。
【讨论】:
【参考方案3】:在泛型类的情况下,由于类型信息在运行时被删除,因此无法检查强制转换。但是您检查列表中的所有对象是否为Waypoint
s,因此您可以使用@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 “