如何在 Kotlin 中使用泛型创建地图?

Ale*_*xey 4 generics kotlin

我需要创建一个 Map,其中键是类,值是适当类的对象。

喜欢:

mapOf<KClass<T>, T>(
    Int::class to 10,
    String::class to "Ten"
)
Run Code Online (Sandbox Code Playgroud)

我想使用泛型来避免“无效”条目,例如 Int::class to "Ten"

我该如何实施?

Rol*_*and 7

我不太确定我是否得到了你真正想要完成的事情。不要忘记泛型在运行时被删除,所以最后你只会有一个Map<KClass<*>, Any>(更正确的是:)Map<Any, Any>。尽管如此,可能最简单的方法是坚持你已经知道的东西。您已经展示了to创建 a的便捷方法 ( ),Pair然后将其传递给mapOf,那么为什么不使用符合您要求的新函数,例如

inline fun <reified T : Any> typedPair(value : T) = Pair(T::class, value)
Run Code Online (Sandbox Code Playgroud)

所以你可以使用:

mapOf(
  typedPair(10), // adds Int::class as key with 10 as value
  typedPair<Short>(1) // adds Short::class as key with 1 as value
  typedPair<Number>(2) // adds Number::class as key with 2 as value
)
Run Code Online (Sandbox Code Playgroud)

当然,通过这种方式,您仍然可以将任何其他星座添加到该地图中。如果你想克服这个问题,你还有一些不同的选择:

创建一个附加typedMapOf功能怎么样,例如:

fun typedMapOf(vararg values : Any) = values.associateBy { it::class }
Run Code Online (Sandbox Code Playgroud)

使用它可能如下所示:

typedMapOf(10, "string", 1.toShort())
Run Code Online (Sandbox Code Playgroud)

但是,您可能很难添加Number::class;-)

您还可以将上面的两个变体混合为以下内容:

data class MyTypedPair<T : Any>(val type : KClass<T>, val value : T)
inline fun <reified T : Any> typedPair(value : T) = MyTypedPair(T::class, value)
fun typedMapOf(vararg values : MyTypedPair<*>) = values.associateBy({it.type}) { it.value }
Run Code Online (Sandbox Code Playgroud)

这基本上现在迫使您提供专门的类型来创建该类型化的地图。

我还有一些其他的变体......你也可以有一个像包装器一样的东西,它只支持最少的一组功能:

class MyValues {
    private val backedMap = mutableMapOf<KClass<*>, Any>()
    fun <T : Any> put(value : T) = backedMap.put(value::class, value)
    operator fun <T : Any> get(key : KClass<T>) = backedMap[key]
}
Run Code Online (Sandbox Code Playgroud)

用法与 略有不同,Map但仍然非常简单:

MyValues().apply {
  put(10)
  put<Short>(1)
}
Run Code Online (Sandbox Code Playgroud)

如果类型不能从值中推导出来,那么您仍然可以使用上述内容来构建一个可能适合您需求的解决方案。