#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) }
🙂