类型键或类键映射

时间:2019-03-17 05:13:14

标签: kotlin

在Kotlin中,我可以使用filterIsInstance获得特定于类型(且类型安全)的子集合:

val misc: List<Any> = listOf(42, 3.14, true, "foo", "bar")
val strings: List<String> = misc.filterIsInstance<String>()
println(strings) // => [foo, bar]

但是我有很多对象,我想按具体类型将其预分类为Map。甚至有可能在Kotlin的文字系统中定义这样的地图吗?

val miscByType: Map<KType, Collection<???>>

或:

val miscByClass: Map<KClass, Collection<???>>

我应该对不安全(但在逻辑上合理)的类型转换使用自定义实现吗?

以下是这样的实现。它可以工作,但是我想知道是否有一种更简单的方法:

import kotlin.reflect.KClass

class InstanceMap {
    // INVARIANT: map from KClass to a set of objects of *that concrete class*
    private val map: MutableMap<KClass<*>, MutableSet<Any>> = mutableMapOf()

    // this is the only public mutator, it guarantees the invariant
    fun add(item: Any): Boolean =
        map.getOrPut(item::class) { mutableSetOf() }.add(item)

    // public non-inline accessor, only needed by the inline accessor
    fun get(cls: KClass<*>): Set<*>? = map[cls]

    // inline accessor that performs an unsafe, but sound, cast
    @Suppress("UNCHECKED_CAST")
    inline fun <reified T> get(): Set<T> = get(T::class) as Set<T>? ?: setOf()
}

fun instanceMapOf(vararg items: Any): InstanceMap = InstanceMap().apply {
    items.forEach { add(it) }
}

val misc = instanceMapOf(42, 3.14, true, "foo", "bar")
val strings = misc.get<String>()
println(strings) // => [foo, bar]

1 个答案:

答案 0 :(得分:2)

您的代码看起来不错。唯一的问题是未经检查的强制转换警告。在JVM字节码级别,由于Java和Kotlin中如何实现泛型的方式,强制类型转换不起作用。也称为类型擦除
https://en.wikipedia.org/wiki/Type_erasure

类型擦除会在您的代码中增加另一个问题-它不会告诉泛型类型参数。例如,List<Int>List<String>List<Map<String, Object>>

具有相同的类

您希望代码在地图中找到超类或接口吗?例如。如果我有

interface A
interface B
class C : A, B
val m = InstanceMap()
m.add(C())

m.get(C::class)
m.get(A::class)
m.get(B::class)

您希望这3次调用返回相同的值吗?

JVM的标准解决方法是显式传递Class<T>参数,并使用Class#cast方法进行强制转换。所做的更改将使代码更安全。

在Kotlin中有一种用于擦除类型的补救措施。您可以添加一个经过修饰的内联函数,以便Kotlin编译器将在内联泛型函数主体中使用确切的类型
https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters

inline fun <reified T> InstanceMap.get() = get(T::class)