Как я могу перебирать массив наборов / карт с различными типами данных

#kotlin

#kotlin

Вопрос:

Я хочу сделать так, чтобы мой код оставался сухим и создавал 3 (или больше, или меньше) кнопки с примерно одинаковой структурой. Итак, я создаю список объектов для перебора и помещаю данные внутри объекта для использования в нескольких местах в AppButton.

Я мог бы подумать, что это слишком Pythonic, потому что это мой основной язык, и я только недавно начал использовать Kotlin. Что я обычно делаю в Python:

 app_buttons = [
    dict(
        text="....",
        icon="....",
        uri_string="....",
    ),
    ...
]

 

Я пробовал что-то подобное в Kotlin с mapOf :

 val appButtons = arrayOf(
    mapOf(
        "title" to getString(R.string.app_btn_example1),
        "icon" to R.drawable.ic_some_icon_1_64,
        "uriString" to "myapp://example1",
    ),
    ...
)
 

а затем перебирать их и получать с карты:

 for (entry in appButtons) {
    buttons.add(
        AppButton(
            entry.get("text"),
            entry.get("icon"),
        ) {
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(entry.get("uriString"))).apply {
                val name = getString(R.string.saved_account_key)
                putExtra(name, sharedPref.getString(name, null))
            }
            startActivity(intent)
        }
    )
}
 

Но потом я получаю Type mismatch. Required String. Found {Comparable amp; java.io.Serializable}? . Я не знаю, какие типы куда поместить…

Хорошо, другой подход, использование setOf и разрушение:

 val appButtons = arrayOf(
    setOf(
        getString(R.string.app_btn_example1),
        R.drawable.ic_some_icon_1_64,
        "myapp://example1",
    ),
    ...
)

for ((text, icon, uriString) in appButtons) {
    buttons.add(
        AppButton(
            text,
            icon
        ) {
            ...
        }
    )
}
 

Но теперь я получаю следующее:

Инициализатор объявления деструктурирования типа Set<{Comparable<*> amp; java.io.Serializable}> должен иметь функцию ‘component1 ()’

Инициализатор объявления деструктурирования типа Set<{Comparable<*> amp; java.io.Serializable}> должен иметь функцию ‘component2 ()’

Инициализатор объявления деструктурирования типа Set<{Comparable<*> amp; java.io.Serializable}> должен иметь функцию ‘component3 ()’

Как мне заставить это работать? Как мне создать базовый список объектов и перебирать их с правильными типами? В Python это так просто. Я явно что-то упускаю.

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

1. Пожалуйста, не используйте карты таким образом. Создайте класс данных с именем ButtonModel или что-то в этом роде.

2. @Sweeper да, вот почему я спрашиваю. Я чувствую, что делаю что-то не так, но я не знаю, что и как это должно быть сделано.

3. Kotlin (во всяком случае, в JVM) — это статически типизированный язык (где типы, Int , String и т.д. известны во время компиляции), Python динамически типизируется (типы проверяются во время выполнения). Это позволяет вам создавать dict s, которые содержат в основном все, что угодно, и вам решать, чтобы убедиться, что извлеченный материал обрабатывается правильно — вы должны знать icon , что это an Int и рассматривать его как один, иначе вы получите сбой во время выполнения. Вы не можете (обычно) делать это на статически типизированных языках — они обеспечивают правильное использование типов, поэтому вы не можете смешивать их на карте. Структура данных, подобная ответу Sweeper, — это то, что вам нужно!

Ответ №1:

Вместо того, чтобы использовать карты, вы должны создать класс данных. Например:

 data class ButtonModel(
     val title: String,
     val icon: Int,
     val uriString: String,
)
 

Затем вы можете создать массив следующим образом:

 val appButtons = arrayOf(
    ButtonModel(
        title = getString(R.string.app_btn_example1),
        icon = R.drawable.ic_some_icon_1_64,
        uriString = "myapp://example1",
    ),
    ...
)
 

Или без меток параметров, если вы предпочитаете:

 val appButtons = arrayOf(
    ButtonModel(
        getString(R.string.app_btn_example1),
        R.drawable.ic_some_icon_1_64,
        "myapp://example1",
    ),
    ...
)
 

Затем, вместо того, чтобы получать их с get помощью или [] , вы можете просто использовать синтаксис dot:

 buttons.add(
    AppButton(
        entry.text,
        entry.icon,
    ) {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(entry.uriString)).apply {
            val name = getString(R.string.saved_account_key)
            putExtra(name, sharedPref.getString(name, null))
        }
        startActivity(intent)
    }
)
 

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

1. В крайнем случае, вы могли бы использовать Triple для этого. Но пользовательский класс намного понятнее, безопаснее и удобочитаем. (Это также очень быстро определяется в Kotlin; это может быть даже однострочный!)