首页 > 解决方案 > 如何使用多个参数化类型(使用 where 子句)以便我可以使用任一参数

问题描述

有没有办法告诉 kotlin 这个函数接受的类型 T 可能是 Long OR Int 或 String?

在这个例子map: Map<String, T>?中可以接收一个 Map。

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>orMap<String, String>值。

所以intent.putExtra(it.key, it.value)不会出现编译错误。

我认为可以使用 where 来做到这一点。

标签: genericskotlin

解决方案


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: 如果您的意思是 map 可以同时包含Int,LongString值,您应该创建一个包装这些类型的对象的类。我还建议向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"
}

推荐阅读