Cast non vérifié en Kotlin : Comment convertir en toute sécurité List<Any?> en List<Waypoint>

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« Types incompatibles » : pourquoi Kotlin crie-t-il sur votre List ?

\n\n

Avez-vous déjà rencontré une situation où Kotlin, ce langage « intelligent » et « sûr », vous affiche soudainement un avertissement jaune Unchecked Cast: kotlin.collections.List<Kotlin.Any?> to kotlin.collections.List<Waypoint> ? Si oui, vous n'êtes pas seul. Cette question sur Stack Overflow a recueilli des milliers de vues, et ce n'est pas surprenant. Quiconque passe de Java ou commence à travailler plus profondément avec les generics en Kotlin finit tôt ou tard par tomber sur ce « râteau ».

\n\n

Dans cet article, nous ne donnerons pas simplement une explication sèche. Nous allons analyser pourquoi cela se produit, montrer trois façons de résoudre correctement le problème et, surtout, comment écrire du code qui ne plantera pas à l'exécution. Prêt ? C'est parti !

\n\n

Le cœur du problème : Type Erasure et « effacement » des types

\n\n

Allons droit au but. Imaginez que vous ayez une fonction qui retourne une liste de n'importe quoi — List<Any?>. Et vous avez besoin d'une liste d'objets spécifiques, par exemple List<Waypoint>. La solution la plus « simple » qui vient à l'esprit est le cast explicite :

\n\n
val rawList: List<Any?> = getDataFromSomewhere() // Supposons que ce soit du JSON ou une base de données\nval waypointList: List<Waypoint> = rawList as List<Waypoint> // Unchecked Cast ! ⚠️
\n\n

Kotlin (et Java sous le capot) utilise le mécanisme de Type Erasure (effacement de type). Cela signifie que pendant l'exécution du programme, l'information sur le type à l'intérieur des generics (ce qui est entre les chevrons) est effacée. Pour la JVM, votre List<Waypoint> n'est qu'une simple List. Le compilateur voit que vous essayez de tromper le système et émet un avertissement : « Hé, je ne peux pas vérifier qu'il y a vraiment des Waypoint là-dedans, tu prends la responsabilité ! ».

\n\n

Si vous êtes sûr à 100% qu'il n'y a que des Waypoint, vous pouvez supprimer l'avertissement avec l'annotation @Suppress("UNCHECKED_CAST"). Mais s'il s'avère qu'il y a, disons, un String, vous obtiendrez une ClassCastException à l'exécution. Et c'est exactement ce contre quoi Kotlin essaie de nous protéger.

\n\n

Ce qu'il NE FAUT PAS faire : les erreurs typiques des débutants

\n\n

Voici quelques exemples que je vois souvent sur les forums. Retenez-les pour ne pas les répéter :

\n
    \n
  • Cast aveugle : list as List<Waypoint> — l'option la plus dangereuse.
  • \n
  • Ignorer l'avertissement : « Ça marche pourtant ! » — ça marche jusqu'à ce que ça plante.
  • \n
  • Tentative de cast en chaîne : (list as? List<Waypoint>) — l'opérateur sûr as? n'aidera pas si les objets à l'intérieur de la liste ne sont pas du bon type. Il vérifie seulement qu'il s'agit d'une List, mais pas son contenu.
  • \n
\n\n

3 méthodes qui fonctionnent : du simple au professionnel

\n\n

Regardons des exemples de code réels. Supposons que nous ayons une classe Waypoint :

\n\n
data class Waypoint(val latitude: Double, val longitude: Double, val name: String)
\n\n

Et une liste « sale » obtenue depuis une API externe :

\n\n
fun fetchRawData(): List<Any?> {\n    return listOf(\n        mapOf("lat" to 55.75, "lon" to 37.62, "name" to "Moscow"),\n        mapOf("lat" to 59.93, "lon" to 30.31, "name" to "SPb")\n    )\n}
\n\n

Méthode 1 : Filtrage avec filterIsInstance (Recommandée ✅)

\n\n

C'est la méthode la plus idiomatique et la plus sûre. La fonction filterIsInstance<T>() filtrera uniquement les éléments qui sont des instances du type spécifié et retournera une liste déjà typée.

\n\n
fun getWaypointsSafely(): List<Waypoint> {\n    val rawData: List<Any?> = fetchRawData()\n    // Supposons que nous convertissions d'une manière ou d'une autre Map en Waypoint\n    val waypoints: List<Waypoint> = rawData\n        .filterIsInstance<Map<*, *>>()\n                

Blogs

Book Recommendations