Generics 如何在Kotlin中使用泛型创建映射?

Generics 如何在Kotlin中使用泛型创建映射?,generics,kotlin,Generics,Kotlin,我需要创建一个映射,其中键是类,值是适当类的对象 比如: mapOf( Int::类到10, 字符串::类到“十” ) 我想使用泛型来避免“无效”条目,比如Int::class to“Ten” 如何实现它?您使用泛型的示例实际上并没有描述该映射的目标。映射接口上的泛型无法描述所需的功能类型。键和值的类型需要封装您放入该映射中的每个键和值,因此,这是可能的: val myInstanceMap = mapOf<KClass<*>, Any>( Int::c

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

比如:

mapOf(
Int::类到10,
字符串::类到“十”
)
我想使用泛型来避免“无效”条目,比如
Int::class to“Ten”


如何实现它?

您使用泛型的示例实际上并没有描述该映射的目标。映射接口上的泛型无法描述所需的功能类型。键和值的类型需要封装您放入该映射中的每个键和值,因此,这是可能的:

val myInstanceMap = mapOf<KClass<*>, Any>(
        Int::class to 10,
        String::class to "10"
)
val myInstanceMap=mapOf(
Int::类到10,
字符串::类到“10”
)
为了获得该映射中特定键和值的类型安全性,您必须自己完成一些工作来包装这样一个通用映射。以下是一个例子:

class ClassToInstanceMap {

    private val backingMap = mutableMapOf<KClass<*>, Any?>()

    operator fun <T: Any> set(key: KClass<T>, value: T) {
        backingMap[key] = value
    }

    @Suppress("UNCHECKED_CAST")
    operator fun <T: Any> get(key: KClass<T>): T {
        return backingMap[key] as T
    }

    fun containsKey(key: KClass<*>): Boolean {
        return backingMap.containsKey(key)
    }

}

fun main() {
    val classToInstanceMap = ClassToInstanceMap()

    classToInstanceMap[Int::class] = 1
    val intInstance = classToInstanceMap[Int::class]
    println(intInstance)

    classToInstanceMap[Int::class] = 2
    val intInstance2 = classToInstanceMap[Int::class]
    println(intInstance2)

    classToInstanceMap[String::class] ="1"
    val stringInstance = classToInstanceMap[String::class]
    println(stringInstance)

    classToInstanceMap[String::class] ="2"
    val stringInstance2 = classToInstanceMap[String::class]
    println(stringInstance2)
}
ClassToInstanceMap类{
private val backingMap=mutableMapOf()
操作员乐趣集(键:KClass,值:T){
backingMap[键]=值
}
@抑制(“未选中的_CAST”)
操作员乐趣获取(键:K类):T{
将backingMap[key]返回为T
}
fun containsKey(键:KClass):布尔值{
返回backingMap.containsKey(键)
}
}
主要内容(){
val classToInstanceMap=classToInstanceMap()
classToInstanceMap[Int::class]=1
val intInstance=classToInstanceMap[Int::class]
println(初始化)
classToInstanceMap[Int::class]=2
val intInstance2=classToInstanceMap[Int::class]
println(初始化2)
classToInstanceMap[String::class]=“1”
val stringInstance=classToInstanceMap[String::class]
println(stringInstance)
classToInstanceMap[String::class]=“2”
val stringInstance2=classToInstanceMap[String::class]
println(stringInstance2)
}

我相信您可以从中找出如何实现映射的其他常规方法。

如果您希望映射在初始化后保持不变,可以执行以下操作:

import kotlin.reflect.KClass

class InstanceKeyMapper(initBlock: InstanceKeyMapper.() -> Unit) {

    private val map = mutableMapOf<KClass<*>, Any>()

    init {
        initBlock(this)
    }

    infix fun <T : Any> KClass<T>.instance(value: T) {
        map[this] = value
    }

    fun toMap() = map as Map<KClass<*>, Any> // downcast to disable mutability

}

fun instanceMapOf(initBlock: InstanceKeyMapper.() -> Unit) = InstanceKeyMapper(initBlock).toMap()
import kotlin.reflect.KClass
类InstanceKeyMapper(initBlock:InstanceKeyMapper.->Unit){
private val map=mutableMapOf()
初始化{
初始化块(此)
}
中缀fun KClass.instance(值:T){
映射[此]=值
}
fun toMap()=映射为映射//向下转换以禁用可变性
}
fun instanceMapOf(initBlock:InstanceKeyMapper.(->Unit)=InstanceKeyMapper(initBlock).toMap()
并按以下方式使用:

fun main(args: Array<String>) {

    val map = instanceMapOf {
        Int::class instance 42 // ok
        String::class instance "abc" // ok
        Float::class instance 3.14f // ok
        Boolean::class instance true // ok
        Long::class instance "not legit" // bad type, compilation error
    }

    println(map[Int::class]) // 2
    println(map[String::class]) // "abc"
    map[Long::class] = 123L // compilation error, read-only
}
fun main(args:Array){
val map=instanceMapOf{
Int::类实例42//ok
String::类实例“abc”//ok
Float::类实例3.14f//ok
布尔::类实例true//ok
Long::类实例“不合法”//类型错误,编译错误
}
println(映射[Int::class])//2
println(map[String::class])/“abc”
map[Long::class]=123L//编译错误,只读
}

我不太确定我是否得到了你真正想要实现的目标。别忘了泛型在运行时会被擦除,所以最后你只需要一个
Map
(更准确地说:
Map
)。尽管如此,可能最简单的方法就是坚持你已经知道的。您已经展示了一种方便的方法(
)来创建
,然后将其传递到
映射的
,那么为什么不使用符合您需求的新函数呢

inline fun <reified T : Any> typedPair(value : T) = Pair(T::class, value)
使用它可以如下所示:

typedMapOf(10, "string", 1.toShort())
但是,您可能很难添加
Number::class
then;-)

您还可以将上述两种变体混合使用,例如:

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 }

如果类型不能从该值派生,那么您仍然可以使用上面的方法构造一个可能适合您需要的解决方案。

我认为这是不可能的,但我希望被证明是错误的。:-)我相信您可以编写一个专门的map类,它可以提供这种类型的安全性。但我不知道有什么现存的。我不知道它是否可以实现现有的
Map
接口。如果将它们放在列表中,您将丢失类型信息。最后,这将是
Map
+1。由于Kotlin在泛型中推断类型的方式,对于
T
,可能有相关但不相等的类型。例如:
put(Number::class,3)
甚至
put(Any::class,3)
。您可以使用一个
具体化的
函数从参数推断类(因此
put(3)
将是
put(3)
),但是如果显式指定类型参数,这仍然可能被误用。最后,您只能在运行时检查实际的相等性,例如,
assert(key==value::class)
。此外,对于类似映射的类,我建议重载
set
get
操作符,以启用语法
map[key]
map[key]=value
@TheOperator关于运算符重载的建议很好。我认为,如果您将@xinaiz构造函数扩展函数生成器构造函数包含在他的答案中,您将拥有一个非常完整的类,它可以满足您的需要。
fun typedMapOf(vararg values : Any) = values.associateBy { it::class }
typedMapOf(10, "string", 1.toShort())
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 }
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]
}
MyValues().apply {
  put(10)
  put<Short>(1)
}