Principais erros de desenvolvedores Kotlin iniciantes: nível avançado
Kotlin é uma linguagem moderna e concisa que está ativamente substituindo Java no desenvolvimento Android e backend. No entanto, sua flexibilidade muitas vezes prega peças em iniciantes que migram de outras linguagens. Neste artigo, analisaremos erros não óbvios, mas críticos, cometidos por desenvolvedores que já dominam a sintaxe básica. Você aprenderá como evitar problemas com corrotinas, classes sealed, funções inline e propriedades delegadas. Cada seção contém exemplos de código específicos e recomendações.
1. Uso incorreto de corrotinas: vazamentos de memória e perda de contexto
Corrotinas são uma ferramenta poderosa para assincronia, mas seu uso incorreto leva a vazamentos de memória e bugs difíceis de detectar. O erro mais comum é iniciar uma corrotina sem vinculá-la ao ciclo de vida do componente.
Erro: escopo global em Activity
// RUIM: a corrotina vive mais que a Activityclass MyActivity : AppCompatActivity() { fun loadData() { GlobalScope.launch { val data = fetchFromNetwork() withContext(Dispatchers.Main) { textView.text = data // Vazamento se a Activity já foi destruída } } }}Solução: uso de lifecycleScope ou viewModelScope
// BOM: a corrotina é cancelada quando a Activity é destruídaclass MyActivity : AppCompatActivity() { fun loadData() { lifecycleScope.launch { val data = withContext(Dispatchers.IO) { fetchFromNetwork() } textView.text = data } }}Dica: Sempre use CoroutineScope vinculado ao ciclo de vida. Para ViewModel — viewModelScope, para Activity/Fragment — lifecycleScope. Nunca use GlobalScope em produção.
2. Abuso de null safety: suposições implícitas
Kotlin é famoso por sua null safety, mas iniciantes frequentemente criam uma falsa sensação de segurança. O erro é usar o operador !! onde chamadas seguras seriam suficientes.
Erro: ignorar tipos anuláveis
// RUIM: pode lançar NullPointerExceptionfun processUser(user: User?) { val name = user!!.name println(name)}Solução: uso do operador elvis ou let
// BOM: extração segurafun processUser(user: User?) { val name = user?.name ?: "Unknown" println(name) // Ou com let: user?.let { u -> println(u.name) }}Importante: O operador !! só se justifica em testes ou quando você tem 100% de certeza que o valor não é null (por exemplo, após uma verificação em if). Nos demais casos, use ?.let, ?: ou requireNotNull().
3. Uso incorreto de classes sealed e when
Classes sealed são uma excelente forma de modelar hierarquias restritas. No entanto, iniciantes frequentemente esquecem que when pode ser usado como expressão e não consideram todos os ramos.
Erro: omitir ramos no when
sealed class Result { data class Success(val data: String) : Result() data class Error(val message: String) : Result() object Loading : Result()}
// RUIM: o compilador não avisará sobre a falta de Loadingfun handleResult(result: Result) { when (result) { is Result.Success -> println(result.data) is Result.Error -> println(result.message) // Loading esquecido! }}Solução: usar when como expressão
// BOM: o compilador forçará o tratamento de todos os ramosfun handleResult(result: Result): String { return when (result) { is Result.Success -> result.data is Result.Error -> result.message is Result.Loa