如何检查 Kotlin 中的泛型类型

问题描述 投票:0回答:6

我正在尝试在 Kotlin 中测试泛型类型。

if (value is Map<String, Any>) { ... }

但是编译器抱怨

无法检查已删除类型的实例:jet.Map

使用普通类型的检查效果很好。

if (value is String) { ... }

使用Kotlin 0.4.68。

我在这里缺少什么?

kotlin generics type-erasure
6个回答
112
投票

问题在于类型参数被删除,因此您无法检查完整类型 Map,因为在运行时没有有关这些 String 和 Any 的信息。

要解决此问题,请使用通配符:

if (value is Map<*, *>) {...}

32
投票

我觉得这样比较合适

inline fun <reified T> tryCast(instance: Any?, block: T.() -> Unit) {
    if (instance is T) {
        block(instance)
    }
}

使用方法

// myVar is nullable
tryCast<MyType>(myVar) {
    // todo with this e.g.
    this.canDoSomething()
}

另一种更短的方法

inline fun <reified T> Any?.tryCast(block: T.() -> Unit) {
    if (this is T) {
        block()
    }
}

使用方法

// myVar is nullable
myVar.tryCast<MyType> {
    // todo with this e.g.
    this.canDoSomething()
}

21
投票

JVM 删除泛型类型信息。但 Kotlin 已经具体化了泛型。如果您有泛型类型 T,您可以将内联函数的类型参数 T 标记为具体化,以便它能够在运行时检查它。

所以你可以这样做:

inline fun <reified T> checkType(obj: Object, contract: T) {
  if (obj is T) {
    // object implements the contract type T
  }
}

1
投票

这是我使用的:

// Use this value if it is of type T, or else use defaultValue
inline fun <reified T> Any?.useIfTypeOrDefault(defaultValue: T) = 
    if (this is T) this else defaultValue

用法(测试):

val map = mapOf("foo" to listOf("cheese"), "bar" to 666)

map["foo"].useIfTypeOrDefault<List<String>>(emptyList()).firstOrNull() shouldBe "cheese"
map["bar"].useIfTypeOrDefault<List<String>>(emptyList()).firstOrNull() shouldBe null

map["foo"].useIfTypeOrDefault<Number>(-1) shouldBe -1
map["bar"].useIfTypeOrDefault<Number>(-1) shouldBe 666

0
投票

我会给出一个解决方案,但我认为它很干净,有点

try{
  (value as Map<String,Any>?)?.let { castedValue ->
     doYourStuffHere() //using castedValue
  }
}catch(e: Exception){
  valueIsNotOfType() //Map<String,Any>
}

-1
投票

我已经用

tryCast<Array<String?>>
尝试了上面的解决方案,我想,在我列出涉及许多演员的特定任务中,这不是一个好主意,因为它大大降低了性能。

这是我最终所做的解决方案 - 手动检查条目和调用方法,如下所示:

 fun foo() {
    val map: Map<String?, Any?> = mapOf()
    map.forEach { entry ->
        when (entry.value) {
            is String -> {
                doSomeWork(entry.key, entry.value as String)
            }
            is Array<*> -> {
                doSomeWork(entry.key, (entry.value as? Array<*>)?.map {
                    if (it is String) {
                        it
                    } else null
                }?.toList())
            }
        }
    }
}


private fun doSomeWork(key: String?, value: String) {

}
private fun doSomeWork(key: String?, values: List<String?>?) {

}
© www.soinside.com 2019 - 2024. All rights reserved.