Unchecked Cast em Kotlin: Como converter List<Any?> para List<Waypoint> com segurança

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

«Tipos incompatíveis»: por que o Kotlin reclama da sua List?

\n\n

Você 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».

\n\n

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\n

O cerne do problema: Type Erasure e o «apagamento» de tipos

\n\n

Vamos 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:

\n\n
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\n

O 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!».

\n\n

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.

\n\n

Como NÃO fazer: erros típicos de iniciantes

\n\n

Aqui 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 seguro as? não ajudará se dentro da lista houver objetos do tipo errado. Ele verificará apenas se é um List, mas não seu conteúdo.
  • \n
\n\n

3 maneiras funcionais: do simples ao profissional

\n\n

Vamos ver exemplos reais de código. Suponha que temos uma classe Waypoint:

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

E temos uma lista «suja» obtida de uma API externa:

\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étodo 1: Filtragem com filterIsInstance (Recomendado ✅)

\n\n

Esta é 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.

\n\n
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                

Blogs

Book Recommendations