我如何在 Kotlin 检查通用类型

我想在 Kotlin 测试一种通用型。

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

但是编译器抱怨说

无法检查被擦除的类型: jet

使用普通类型的检查工作得很好。

if (value is String) { ... }

使用 Kotlin 0.4.68。

我错过了什么?

46261 次浏览

The problem is that type arguments are erased, so you can't check against the full type Map, because at runtime there's no information about those String and Any.

To work around this, use wildcards:

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

JVM removes the generic type information. But Kotlin has reified generics. If you have a generic type T, you can mark type parameter T of an inline function as reified so it will be able to check it at runtime.

So you can do:

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

I think this is more appropriate way

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

Usage

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

Another shorter approach

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

Usage

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

I have tried the solution above with tryCast<Array<String?>> and, I guess, in my specific task in listing with many castings involved it was no so great idea, because it was slowing the performance drastically.

This is the solution I did finally - manually check the entries and call methods, like this:

 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?>?) {


}

I'm gonna give a workaround solution but I think its clean, kind of

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