99 votes

Comment vérifier le type générique en Kotlin ?

J'essaie de tester un type générique en Kotlin.

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

Mais le compilateur se plaint avec

Impossible de vérifier l'existence d'une instance du type effacé : jet.Map

Le contrôle avec un type normal fonctionne bien.

if (value is String) { ... }

Kotlin 0.4.68 est utilisé.

Qu'est-ce que je rate ici ?

105voto

Andrey Breslav Points 1944

Le problème est que les arguments de type sont effacés, ce qui fait que vous ne pouvez pas vérifier le type complet de Map, car au moment de l'exécution, il n'y a aucune information sur ces String et Any.

Pour contourner ce problème, utilisez des caractères génériques :

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

25voto

V. Kalyuzhnyu Points 1515

Je pense que cette façon est plus appropriée

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

Utilisation

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

Une autre approche plus courte

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

Utilisation

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

20voto

menno Points 259

La JVM supprime les informations sur le type générique. Mais Kotlin a réifié les génériques. Si vous avez un type générique T, vous pouvez marquer le paramètre de type T d'une fonction inline comme étant réifié afin de pouvoir le vérifier au moment de l'exécution.

Donc vous pouvez le faire :

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

0voto

Antoine El Murr Points 297

Je vais donner une solution de contournement, mais je pense que c'est propre, en quelque sorte.

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

-1voto

J'ai essayé la solution ci-dessus avec tryCast<Array<String?>> et, je suppose que dans ma tâche spécifique de listing avec de nombreux castings impliqués, ce n'était pas une si bonne idée, car cela ralentissait drastiquement les performances.

C'est la solution que j'ai finalement adoptée - vérifier manuellement les entrées et appeler les méthodes, comme ceci :

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

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X