Kotlin: Как работать с приведением списков: Не отмечено Приведение: kotlin.collections.List <Kotlin.Any?> В ​​kotlin.colletions.List <Waypoint>


109

Я хочу написать функцию, которая возвращает каждый элемент в 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>я получаю предупреждение:

Не отмечено флажком Приведение: kotlin.collections.List в kotlin.colletions.List

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

Ответы:


195

В Kotlin нет возможности проверить общие параметры во время выполнения в общем случае (например, просто проверить элементы a List<T>, что является только особым случаем), поэтому приведение универсального типа к другому с другими универсальными параметрами вызовет предупреждение, если только cast находится в пределах дисперсии .

Однако есть разные решения:

  • Вы проверили тип и совершенно уверены, что приведение безопасно. Учитывая это, вы можете подавить предупреждение с помощью @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

6
Отличный ответ! Я выбираю решение list.filterIsInstance <Waypoint> (), потому что считаю его самым чистым решением.
Лукас Лехнер,

4
Обратите внимание, что если вы используете, filterIsInstanceа исходный список содержит элементы другого типа, ваш код автоматически отфильтрует их. Иногда это то, что вам нужно, но иногда вы можете получить такой же IllegalStateExceptionили подобный бросок. В последнем случае вы можете создать свой собственный метод для проверки, а затем inline fun <reified R> Iterable<*>.mapAsInstance() = map { it.apply { check(this is R) } as R }
применить

3
Обратите внимание, что .applyлямбда не возвращает возвращаемое значение, а возвращает полученный объект. Вы, вероятно, захотите использовать, .takeIfесли хотите, чтобы опция возвращала нуль.
bj0 08

10

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

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

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


3

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

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


1

Я немного изменил ответ @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

Cannot access 'Serializable': it is internal in 'kotlin.io'
Искал

0

Вместо того

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.