¿Cómo puedo verificar el tipo genérico en Kotlin?

84

Estoy tratando de probar un tipo genérico en Kotlin.

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

Pero el compilador se queja con

No se puede comprobar, por ejemplo, el tipo borrado: jet.Map

La comprobación con un tipo normal funciona bien.

if (value is String) { ... }

Se utiliza Kotlin 0.4.68.

¿Que me estoy perdiendo aqui?

Philipp Brüll
fuente

Respuestas:

92

El problema es que los argumentos de tipo se borran, por lo que no se puede comparar con el mapa de tipo completo, porque en el tiempo de ejecución no hay información sobre esos String y Any.

Para solucionar este problema, utilice comodines:

if (value is Map<*, *>) {...}
Andrey Breslav
fuente
¡Excelente! ¡Eso funciona perfectamente! Me confundió el ejemplo en la documentación: confluence.jetbrains.net/display/Kotlin/Type+casts
Philipp Brüll
51
¿Qué pasa si realmente quieres comprobar que algo es un Collection<String>lanzamiento automático?
Beruic
Tengo un fragmento como este if (it.getSerializable(ARG_PARAMS) is HashMap<*, *>) {it.getSerializable(ARG_PARAMS) as HashMap<String, String>} else null. Así que básicamente se va a tratar de fundición HashMap<String, Integer>a HashMap<String, String>si yo estoy comprobando contra el tipo genérico. ¿Me estoy perdiendo de algo?
Farid
@FARID Sí, lo hará, y este tipo de elenco no es seguro
Andrey Breslav
18

JVM elimina la información de tipo genérico. Pero Kotlin ha cosificado los genéricos. Si tiene un tipo genérico T, puede marcar el parámetro de tipo T de una función en línea como reificado para que pueda verificarlo en tiempo de ejecución.

Entonces puedes hacer:

inline fun <reified T> checkType(obj: Object, contract: T) {
  if (obj is T) {
    // object implements the contract type T
  }
}
menno
fuente
3
¿Podría mostrarnos un ejemplo de cómo llamar checkType()? No estoy seguro de qué pasar por el segundo argumento.
Michael Osofsky
10

Creo que esta es la forma más apropiada

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

Uso

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

Otro enfoque más corto

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

Uso

// myVar is nullable
myVar.tryCast<MyType> {
    // todo with this e.g.
    this.canDoSomething()
}
Vlad
fuente
1
Por qué algo como esto no está disponible directamente en kotlin stdlib :-(
ATom
¿No es lo something as? Stringmismo? Tenga en cuenta el signo de interrogación después de as?
Dalibor Filus
@DaliborFilus no. Se trata de genéricos y tipos borrados durante el tiempo de ejecución. Si no tiene que lidiar con Genéricos, simplemente puede usar as?, corregir.
stk
0

Probé la solución anterior con tryCast<Array<String?>>y, supongo, en mi tarea específica de enumerar con muchos castings involucrados no fue tan buena idea, porque estaba ralentizando drásticamente el rendimiento.

Esta es la solución que hice finalmente: verifique manualmente las entradas y los métodos de llamada, como este:

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

}
Varvara Solovyeva
fuente