как использовать более одного параметризованного типа (используя предложение where), чтобы я мог использовать любой параметр - PullRequest
0 голосов
/ 14 октября 2019

Есть ли способ сообщить kotlin, что тип T, принятый этой функцией, может быть Long OR Int или String?

В этом примере map: Map<String, T>? может получить карту.

import android.content.Context
import android.content.Intent

const val KEY_COMMAND = "KEY_COMMAND"
const val KEY_EXTRA = "KEY_EXTRA"

class IntentBuilder {
    companion object {

        fun <T> getIntent(
            context: Context,
            clazz: Class<Any>,
            command: Command,
            map: Map<String, T>?
        ): Intent where T : Long {
            val intent = Intent(context, clazz)
            intent.putExtra(KEY_COMMAND, command)
            if (map != null) {
                map.entries.forEach {
                    intent.putExtra(it.key, it.value)
                }
            }
        }
    }
}

Я бы хотел, чтобы эта функция получила значение Map <String, Long> или Map<String, String>.

Так что intent.putExtra(it.key, it.value) не приведет к ошибке компиляции.

Я думал, что это можно будет сделать, используя where.

1 Ответ

1 голос
/ 14 октября 2019

Вы можете решить свою проблему, инкапсулировав getIntent в универсальный класс с закрытым конструктором и предоставив его экземпляры, параметризованные с допустимыми типами:

class IntentFactory<in T> private constructor() {
    companion object {
        val int = IntentFactory<Int>()
        val long = IntentFactory<Long>()
        val string = IntentFactory<String>()
    }

    fun getIntent(
        context: Context,
        clazz: Class<*>,
        command: Command,
        map: Map<String, T>?
    ): Intent {
        // your implementation
    }
}

Вариант использования:

IntentFactory.int.getIntent(
    context,
    clazz,
    command,
    mapOf("1" to 1)
)

UPD: Если вы имеете в виду, что карта может содержать значения Int, Long и String одновременно, вы должны создать класс, который охватывает объекты этих типов. Я также предлагаю добавить key свойство к этому классу:

class IntentExtra<out T> private constructor(val key: String, val value: T) {
    companion object {
        operator fun invoke(key: String, value: Int) = IntentExtra(key, value)
        operator fun invoke(key: String, value: Long) = IntentExtra(key, value)
        operator fun invoke(key: String, value: String) = IntentExtra(key, value)
    }
}

Теперь ваша функция может принимать List<IntentExtra<*>>:

fun getIntent(
    context: Context,
    clazz: Class<*>,
    command: Command,
    extra: List<IntentExtra<*>>?
): Intent {
    // your implementation
}

Вариант использования:

IntentFactory.getIntent(
    context,
    clazz,
    command,
    listOf(
        IntentExtra("int", 1),
        IntentExtra("long", 1L),
        IntentExtra("string", "s")
    )
)

Вы также можете создать DSL, чтобы сделать клиентский код немного чище:

class IntentExtraDSL(private val intent: Intent) {
    private fun extra(key: String, value: Any) {
        intent.putExtra(key, value)
    }

    infix fun String.extra(value: Int) = extra(this, value)
    infix fun String.extra(value: Long) = extra(this, value)
    infix fun String.extra(value: String) = extra(this, value)
}
fun getIntent(
    context: Context,
    clazz: Class<*>,
    command: Command,
    extra: IntentExtraDSL.() -> Unit
): Intent {
    val intent = Intent(context, clazz)
    intent.putExtra(KEY_COMMAND, command)
    IntentExtraDSL(intent).extra()
    return intent
}

Вариант использования:

IntentFactory.getIntent(
    context,
    clazz,
    command
) {
    "int" extra 1
    "long" extra 1L
    "string" extra "s"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...