Проверка или приведение к от Kotlin Любой к - PullRequest
2 голосов
/ 11 апреля 2019

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

Ответы [ 2 ]

5 голосов
/ 11 апреля 2019

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

Основная проблема заключается в том, что IntArray - это 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.

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

2 голосов
/ 11 апреля 2019

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...