«Tipos incompatíveis»: por que o Kotlin reclama da sua List?
\n\nVocê já se deparou com uma situação em que o Kotlin, essa linguagem «inteligente» e «segura», de repente exibe um aviso amarelo Unchecked Cast: kotlin.collections.List<Kotlin.Any?> to kotlin.collections.List<Waypoint>? Se sim — você não está sozinho. Essa pergunta no Stack Overflow acumulou milhares de visualizações, e não é surpresa. Qualquer pessoa que migra do Java ou começa a trabalhar mais profundamente com genéricos em Kotlin, mais cedo ou mais tarde, esbarra nesse «problema».
Neste artigo, não vamos apenas dar uma explicação superficial. Vamos analisar por que isso acontece, mostrar três maneiras de resolver o problema corretamente e, o mais importante — como escrever código que não quebre em tempo de execução. Prontos? Vamos lá!
\n\nO cerne do problema: Type Erasure e o «apagamento» de tipos
\n\nVamos direto ao ponto. Imagine que você tem uma função que retorna uma lista de qualquer coisa — List<Any?>. Mas você precisa de uma lista de objetos específicos, por exemplo, List<Waypoint>. A solução mais «simples» que vem à mente é a conversão explícita:
val rawList: List<Any?> = getDataFromSomewhere() // Suponha que seja JSON ou um banco de dados\nval waypointList: List<Waypoint> = rawList as List<Waypoint> // Unchecked Cast! ⚠️\n\nO Kotlin (e o Java por baixo dos panos) usa o mecanismo de Type Erasure (apagamento de tipos). Isso significa que, durante a execução do programa, a informação sobre o tipo dentro dos genéricos (o que está entre colchetes angulares) é apagada. Para a JVM, seu List<Waypoint> é apenas um List. O compilador percebe que você está tentando enganar o sistema e emite um aviso: «Ei, não posso verificar se realmente há Waypoint lá, você está assumindo a responsabilidade!».
Se você tem 100% de certeza de que só há Waypoint lá, pode suprimir o aviso com a anotação @Suppress("UNCHECKED_CAST"). Mas se houver, por exemplo, uma String — você receberá um ClassCastException em tempo de execução. E é exatamente disso que o Kotlin tenta nos proteger.
Como NÃO fazer: erros típicos de iniciantes
\n\nAqui estão alguns exemplos que vejo frequentemente em fóruns. Lembre-se deles para não repetir:
\n- \n
- Conversão cega:
list as List<Waypoint>— a opção mais perigosa. \n - Ignorar o aviso: «Está funcionando!» — funciona, até quebrar. \n
- Tentar converter em cadeia:
(list as? List<Waypoint>)— o operador seguroas?não ajudará se dentro da lista houver objetos do tipo errado. Ele verificará apenas se é umList, mas não seu conteúdo. \n
3 maneiras funcionais: do simples ao profissional
\n\nVamos ver exemplos reais de código. Suponha que temos uma classe Waypoint:
data class Waypoint(val latitude: Double, val longitude: Double, val name: String)\n\nE temos uma lista «suja» obtida de uma API externa:
\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\nMétodo 1: Filtragem com filterIsInstance (Recomendado ✅)
\n\nEsta é a maneira mais idiomática e segura. A função filterIsInstance<T>() filtra apenas os elementos que são instâncias do tipo especificado e retorna uma lista já tipificada.
fun getWaypointsSafely(): List<Waypoint> {\n val rawData: List<Any?> = fetchRawData()\n // Suponha que de alguma forma convertemos Map em Waypoint\n val waypoints: List<Waypoint> = rawData\n .filterIsInstance<Map<*, *>>()\n