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<*&g
To improve @hotkey's answer here's my solution:
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.
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.
I made a little variation to @hotkey answer when used to check Serializable to List objects :
@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
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 Waypoint
s so you can just suppress the warning with @Suppress("UNCHECKED_CAST")
.
To avoid such warnings you have to pass a List
of 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.
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