Я пытаюсь построить функцию, которая принимает 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, тогда я могу использовать {{X5 }} или другая конструкция цикла / отображения для вывода отдельных элементов. Но я не могу найти способ сделать это.

2
Crystal 11 Апр 2019 в 21:14

2 ответа

Лучший ответ

arrayOf вернуть Array<T>. Если вы хотите использовать IntArray, а не Array<Int>, вы ищете intArrayOf.

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

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

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

Как уже упоминалось в другом ответе, вы можете использовать рекурсивный подход. И, как я уже упоминал, оператор when исключает IntArray, FloatArray и подобные из операторов multi-catch когда. Для них нужны отдельные ветки, если вы планируете их обрабатывать. Кроме того, массив также не является итерируемым, что означает, что вам также нужна отдельная ветвь для него. К счастью, вам не нужны ветви для каждого из универсальных типов, хотя в процессе вы обнаружите, что 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 должно заставить работать умный актерский состав. Если вы объединяете их в одну ветку, она разрывается, потому что не может определить тип. Это означает, что вы возвращаетесь к Any, который автоматически не имеет метода iterator(), который предотвращает как value.forEach, так и for(item in value). Отсутствие общего интерфейса для IntArray, FloatArray и т. П. Не облегчает эту задачу.

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

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

5
Zoe 11 Апр 2019 в 20:55

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

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

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

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

3
Wietbot 11 Апр 2019 в 18:27