„Inkompatible Typen“: Warum schreit Kotlin Ihre Liste an?
\n\nHaben Sie jemals die Situation erlebt, in der Kotlin, diese „intelligente“ und „sichere“ Sprache, plötzlich eine gelbe Warnung ausgibt: Unchecked Cast: kotlin.collections.List<Kotlin.Any?> to kotlin.collections.List<Waypoint>? Wenn ja – Sie sind nicht allein. Diese Frage auf Stack Overflow hat tausende Aufrufe gesammelt, und das ist nicht verwunderlich. Jeder, der von Java umsteigt oder tiefer mit Generics in Kotlin arbeitet, stolpert früher oder später über diesen „Stolperstein“.
In diesem Artikel geben wir nicht nur eine trockene Erklärung. Wir analysieren, warum das passiert, zeigen drei Wege, das Problem richtig zu lösen, und vor allem – wie man Code schreibt, der zur Laufzeit nicht explodiert. Bereit? Los geht‘s!
\n\nDer Kern des Problems: Type Erasure und das „Löschen“ von Typen
\n\nKommen wir gleich zur Sache. Stellen Sie sich vor, Sie haben eine Funktion, die eine Liste von irgendetwas zurückgibt – List<Any?>. Sie benötigen aber eine Liste konkreter Objekte, z. B. List<Waypoint>. Die „einfachste“ Lösung, die einem in den Sinn kommt, ist die explizite Umwandlung:
val rawList: List<Any?> = getDataFromSomewhere() // Angenommen, das ist JSON oder eine Datenbank\nval waypointList: List<Waypoint> = rawList as List<Waypoint> // Unchecked Cast! ⚠️\n\nKotlin (und Java unter der Haube) verwendet den Mechanismus Type Erasure (Typenlöschung). Das bedeutet, dass zur Laufzeit des Programms die Typinformation innerhalb der Generics (das in den spitzen Klammern) gelöscht wird. Für die JVM ist Ihre List<Waypoint> einfach eine List. Der Compiler sieht, dass Sie versuchen, das System zu täuschen, und gibt eine Warnung aus: „Hey, ich kann nicht überprüfen, ob da wirklich Waypoint drin ist, du übernimmst die Verantwortung!“.
Wenn Sie zu 100% sicher sind, dass nur Waypoint drin ist, können Sie die Warnung mit der Annotation @Suppress("UNCHECKED_CAST") unterdrücken. Sollte sich aber doch, sagen wir, ein String darin befinden – erhalten Sie zur Laufzeit eine ClassCastException. Und genau davor versucht uns Kotlin zu schützen.
Wie man es NICHT machen sollte: Typische Anfängerfehler
\n\nHier sind einige Beispiele, die ich oft in Foren sehe. Merken Sie sie sich, um sie nicht zu wiederholen:
\n- \n
- Blinde Umwandlung:
list as List<Waypoint>– die gefährlichste Variante. \n - Ignorieren der Warnung: „Es funktioniert doch!“ – es funktioniert, bis es abstürzt. \n
- Versuch der Umwandlung in einer Kette:
(list as? List<Waypoint>)– der sichere Operatoras?hilft nicht, wenn die falschen Objekte in der Liste sind. Er prüft nur, ob es eineListist, aber nicht deren Inhalt. \n
3 funktionierende Wege: Vom Einfachen zum Professionellen
\n\nSchauen wir uns reale Codebeispiele an. Angenommen, wir haben eine Klasse Waypoint:
data class Waypoint(val latitude: Double, val longitude: Double, val name: String)\n\nUnd eine „schmutzige“ Liste, die von einer externen API stammt:
\n\nfun 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\nWeg 1: Filtern mit filterIsInstance (Empfohlen ✅)
\n\nDies ist der idiomatischste und sicherste Weg. Die Funktion filterIsInstance<T>() filtert nur die Elemente heraus, die Instanzen des angegebenen Typs sind, und gibt eine bereits typisierte Liste zurück.
fun getWaypointsSafely(): List<Waypoint> {\n val rawData: List<Any?> = fetchRawData()\n // Angenommen, wir wandeln Map irgendwie in Waypoint um\n val waypoints: List<Waypoint> = rawData\n .filterIsInstance<Map<*, *>>()\n