Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.5k views
in Technique[技术] by (71.8m points)

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

I want to write a function that returns every item in a List that 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:

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:

Unchecked Cast: kotlin.collections.List to 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

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 warning with @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 list and 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
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...