Kotlin: Как работать со списками: Непроверено В ролях: kotlin.collections.List <Kotlin.Any?> To kotlin.colletions.List <Путевая точк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> я получаю предупреждение:

Unchecked Cast: kotlin.collections.List to kotlin.colletions.List

Я не могу найти способ реализовать его в противном случае. Какой правильный способ реализовать эту функцию без этого предупреждения?

Ответы

Ответ 1

В Kotlin нет способа проверить общие параметры во время выполнения в общем случае (например, просто проверять элементы 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 { ... } line), поэтому он не будет заметным.

  • Напишите функцию утилиты, которая проверяет тип и возвращает тот же список, если тип верен, тем самым инкапсулируя листинг (все еще не установленный с точки зрения компилятора) внутри него:

    @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
    

Ответ 2

В случае родовых классов отбрасывание не может быть проверено, поскольку информация о типе стирается во время выполнения. Но вы проверяете, что все объекты в списке Waypoint, поэтому вы можете просто подавить предупреждение с помощью @Suppress("UNCHECKED_CAST").

Чтобы избежать таких предупреждений, вы должны передать List объектов, конвертируемых в Waypoint. Когда вы используете *, но, пытаясь получить доступ к этому списку в виде типизированного списка, вам всегда понадобится листинг, и этот прилив будет снят.

Ответ 3

Чтобы улучшить @hotkey, ответьте здесь на мое решение:

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

Это дает вам List<Waypoint> если все элементы могут быть отлиты, в противном случае - null.

Ответ 4

Я сделал небольшое изменение в ответе @hotkey, когда он использовался для проверки Serializable to List объектов:

    @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

Ответ 5

Вместо

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

Мне нравится делать

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

Не уверен, насколько это эффективно, но по крайней мере никаких предупреждений.