Котлін: Як працювати зі списками: Неперевірена ролях: kotlin.collections.List <Kotlin.Any?> До kotlin.colletions.List <Waypoint>


108

Я хочу написати функцію, яка повертає кожен елемент у a, Listякий не є першим чи останнім елементом (a via point). Функція отримує загальну 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 to kotlin.colletions.List

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

Відповіді:


191

У Котліні не існує можливості перевірити загальні параметри під час виконання (як-от просто перевірка елементів а 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 { ... }рядку), тому не буде помітний.

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

    @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

10

Щоб покращити відповідь @ hotkey ось моє рішення:

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

Це дає вам можливість, List<Waypoint>якщо всі предмети можуть бути закинуті, інакше нульове.


3

У разі загальних класів касти неможливо перевірити, оскільки інформація про тип стирається під час виконання. Але ви перевіряєте, що всі об'єкти у списку є Waypoints, щоб ви могли просто придушити попередження @Suppress("UNCHECKED_CAST").

Щоб уникнути подібних попереджень, вам потрібно передати Listоб'єкти, до яких можна конвертуватись Waypoint. Коли ви використовуєте, *але намагаєтесь отримати доступ до цього списку як введений список, вам завжди потрібен акторський склад, і цей склад буде знятий з поля.


1

Я трохи змінив відповідь @hotkey, коли використовувався для перевірки Serializable для Список об'єктів:

    @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.