Проверка или приведение из Kotlin любого в

#android #kotlin

#Android #kotlin

Вопрос:

Я пытаюсь создать функцию, которая принимает a Map<String, Any> в Kotlin. В функции я хотел бы проверить тип, а затем выполнить логику для него в зависимости от типа. Чтобы люди могли переходить к картам функций, которые выглядели как:

 "key": 1 (int)
"key": 1.2 (double)
"key": "someString" (string)
"key": [1, 2, 3] (array of ints)
"key": ["apple", "banana", "cutie"] (array of strings)
  

Я хочу проверить тип и привести его к типу, чтобы я мог использовать либо только одно значение, либо массив значений.

Пока я могу выполнять отдельные значения:

 when (value) {
    is String -> doWork(value)
    is Int -> doWork(value)
    is Float -> doWork(value)
    is Double -> doWork(value)
    is IntArray -> ???
    else -> {
        // ???
    }
}
  

Я довольно новичок в Kotlin, но когда я вызываю эту функцию с

 foo(mapOf("test_track" to arrayOf(1, 2, 3))
  

Блок when переходит к else, потому что ArrayOf возвращает Array<Int> значение, которое не является intArray . И моя конечная цель — иметь возможность проверять наличие этих типов Array<Int> , Array<String> , или даже List<Int> , и если что-то есть Collection , тогда я могу использовать forEach или другую конструкцию цикла / сопоставления для извлечения отдельных элементов. Но, похоже, я не могу найти способ сделать это.

Комментарии:

1. Тип. Удаление. К сожалению, Kotlin не застрахован.

Ответ №1:

arrayOf возвращает Array<T> . Если вам нужен intArray, а не an Array<Int> , вы ищете intArrayOf .

Основная проблема здесь заключается в том, что an — IntArray это an int[] , где в качестве an Array<T> преобразуется в целое число[] . И здесь вы, вероятно, можете сказать, почему это не удается: An int[] не является экземпляром Integer[] , и наоборот. Или, по крайней мере, сбой базового преобразования.

Наблюдаемые типы Array и intArray также отличаются, но именно разница во времени выполнения объясняет, почему он ломается.

Если вы намерены также принимать массивы, такие как intArray и FloatArray (примитивные массивы), проблема в том, что у них нет общего суперкласса. Нет коллекции, нет итерации, только Any . Это означает, что вы не можете выполнить ветку when с несколькими типами для них.

Однако, как упоминалось в другом ответе, вы можете использовать рекурсивный подход. И, как я уже упоминал, оператор when исключает intArray, FloatArray и аналогичные из операторов multi-catch when . Для этого нужны отдельные ветки, если вы планируете их обрабатывать. Кроме того, массив также не является итерируемым, что означает, что для него тоже нужна отдельная ветвь. К счастью, вам не нужны ветви для каждого из универсальных типов, хотя вы обнаружите, что Any? в процессе.

 // This function is just here to show what I've based this off (and make any misunderstandings visible early)
fun processMap(map: Map <String, Any>) {
    for ((key, value) in map) {
        /*return value(s) = */
        processValue(value);
    }
}

fun processValue(value: Any?) {
    when (value) {
        // ... other types. I'm going to assume doWork acts differently based on types, but
        // if it is the same function and there are no type differences, you can
        // merge them into a single branch with:
        //
        // is String, is Int, is Float, is Double -> doWork(value)
        // ...
        is String -> doWork(value)
        is Int -> doWork(value)
        is Float -> doWork(value)
        is Double -> doWork(value)
        // ...
        // Process lists, collections, and other Iterables. 
        is Iterable<*> -> value.forEach { processValue(it) }
        // Process Arrays
        is Array<*> -> value.forEach { processValue(it) }
        // Process primitive arrays
        is IntArray -> value.forEach { processValue(it) }
        is FloatArray -> value.forEach { processValue(it) }
        // And so on

    }
}
  

Если вы планируете обрабатывать карты, вы можете использовать is Map<*, *> и обрабатывать свои элементы любым удобным для вас способом.

Разделение Array , IntArray , и FloatArray заключается в том, чтобы заставить работать smart cast. Если вы объединяете их в одну ветку, она ломается, потому что не может определить тип. Это означает, что вы возвращаетесь к an Any , у которого автоматически нет iterator() метода an, который предотвращает оба value.forEach и for(item in value) . Отсутствие общего интерфейса для IntArray , FloatArray , и подобных, не делают это проще.

Оба они создают один и тот же экземпляр, поэтому, т.е. Array<Int>.forEach и IntArray.forEach оба будут запускать is Int ветвь вашего оператора when .

Связанные ресурсы

Ответ №2:

Что вы можете сделать, так это перебирать элементы итеративного и обрабатывать элементы.

 fun doAction(value: Any?)
{
    when (value)
    {
        is Iterable<*> -> {
            value.forEach {
                doAction(it)
            }
        }
    }
}
  

Это означает, что вы не можете выполнить специальное действие для массива, но вы можете обработать все его элементы.

Это может быть жизнеспособным решением для вашего сценария.

Комментарии:

1. или короче (value as? Iterable<*>)?.forEach { doAction(it) } 🙂