Neste tutorial, voce vai aprender as boas praticas mais importantes ao escrever codigo Kotlin. Vamos cobrir convencoes de nomenclatura, formatacao, padroes idiomaticos e ferramentas de analise estatica como ktlint e detekt. Seguir essas praticas torna o codigo mais legivel, mais facil de manter e alinhado com o que a comunidade Kotlin espera. Se voce ja conhece o basico da linguagem — variaveis e tipos, funcoes e classes — esta pronto para dar esse proximo passo.
Convencoes de Nomenclatura
A primeira coisa que qualquer pessoa percebe ao ler codigo alheio sao os nomes. Kotlin segue convencoes muito parecidas com as do Java, mas com algumas particularidades.
Pacotes devem usar letras minusculas e sem underscores:
// Bom
package com.meuapp.dominio.usuario
// Ruim
package com.meuApp.Dominio.Usuario
Classes e interfaces usam PascalCase. Nomes de data classes seguem a mesma regra:
// Bom
class ContaBancaria
data class Endereco(val rua: String, val numero: Int)
interface Repositorio
// Ruim
class conta_bancaria
data class endereco(val rua: String, val numero: Int)
Funcoes, propriedades e variaveis locais usam camelCase:
// Bom
fun calcularDesconto(valor: Double): Double { ... }
val nomeCompleto = "Maria Silva"
// Ruim
fun CalcularDesconto(valor: Double): Double { ... }
val nome_completo = "Maria Silva"
Constantes (propriedades const val ou valores em companion objects que sao verdadeiramente constantes) usam SCREAMING_SNAKE_CASE:
companion object {
const val TAXA_MAXIMA = 0.15
const val TEMPO_LIMITE_MS = 5000L
}
Uma dica que vale ouro: evite abreviacoes obscuras. Prefira quantidadeDeItens a qtdItens. O codigo e lido muito mais vezes do que e escrito, entao investir em nomes claros economiza tempo de todo mundo.
Formatacao e Ferramentas de Analise
Manter um estilo consistente em todo o projeto e fundamental, especialmente quando ha mais de uma pessoa contribuindo. Duas ferramentas se destacam no ecossistema Kotlin.
ktlint
O ktlint e um linter e formatador que aplica as convencoes oficiais de estilo do Kotlin. Ele pode ser adicionado ao projeto via Gradle:
// build.gradle.kts
plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
}
Com isso, voce pode rodar ./gradlew ktlintCheck para verificar o estilo e ./gradlew ktlintFormat para corrigir automaticamente. O ideal e integrar isso ao pipeline de CI para que nenhum codigo fora do padrao entre no repositorio.
detekt
Enquanto o ktlint cuida da formatacao, o detekt vai alem e faz analise estatica de qualidade. Ele detecta code smells como funcoes muito longas, complexidade ciclomatica alta, magic numbers e muito mais.
// build.gradle.kts
plugins {
id("io.gitlab.arturbosch.detekt") version "1.23.4"
}
detekt {
config.setFrom("$projectDir/config/detekt/detekt.yml")
buildUponDefaultConfig = true
}
Usar as duas ferramentas juntas garante que o codigo esta tanto bem formatado quanto saudavel em termos de qualidade.
Prefira when no Lugar de Cadeias de if-else
Uma das marcas registradas do Kotlin idiomatico e o uso de expressoes when no lugar de longas cadeias de if-else. Alem de ser mais legivel, o when funciona como expressao, ou seja, retorna um valor.
// Ruim: cadeia de if-else
fun classificarNota(nota: Int): String {
if (nota >= 9) {
return "Excelente"
} else if (nota >= 7) {
return "Bom"
} else if (nota >= 5) {
return "Regular"
} else {
return "Insuficiente"
}
}
// Bom: when como expressao
fun classificarNota(nota: Int): String = when {
nota >= 9 -> "Excelente"
nota >= 7 -> "Bom"
nota >= 5 -> "Regular"
else -> "Insuficiente"
}
Quando voce combina when com sealed classes, o compilador garante que todos os casos foram tratados, eliminando a necessidade do bloco else e protegendo contra esquecimentos.
Use Data Classes para Dados
Se uma classe existe primariamente para armazenar dados, ela deve ser uma data class. O compilador gera automaticamente equals(), hashCode(), toString(), copy() e funcoes de destructuring, eliminando codigo boilerplate.
// Ruim: classe regular para dados
class Usuario(val nome: String, val email: String) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Usuario) return false
return nome == other.nome && email == other.email
}
override fun hashCode(): Int = 31 * nome.hashCode() + email.hashCode()
override fun toString(): String = "Usuario(nome=$nome, email=$email)"
}
// Bom: data class
data class Usuario(val nome: String, val email: String)
A versao com data class faz exatamente a mesma coisa que a versao manual, so que em uma unica linha. Menos codigo significa menos bugs.
Scope Functions: use com Criterio
Kotlin oferece cinco scope functions — let, run, with, apply e also. Cada uma tem seu caso de uso ideal, e a chave e nao exagerar. Se voce quer se aprofundar, veja o tutorial de lambdas.
// Bom: let para null check
val comprimento = nome?.let {
println("Processando: $it")
it.length
}
// Bom: apply para configurar objetos
val textView = TextView(context).apply {
text = "Ola, mundo"
textSize = 16f
setTextColor(Color.BLACK)
}
// Ruim: aninhar scope functions desnecessariamente
val resultado = valor?.let { v ->
v.toString().let { s ->
s.uppercase().also { u ->
println(u)
}
}
}
A regra de bolso e: se o aninhamento dificulta a leitura, quebre em variaveis intermediarias. Legibilidade sempre vem primeiro.
Evite Tipos Nullable Desnecessarios
O sistema de null safety do Kotlin e uma das suas maiores vantagens. Porem, muita gente vinda do Java acaba usando ? em tudo por habito. Pergunte-se sempre: essa propriedade realmente pode ser nula?
// Ruim: nullable sem necessidade
class Pedido(
val id: Long?,
val descricao: String?,
val valor: Double?
)
// Bom: so nullable quando faz sentido
class Pedido(
val id: Long,
val descricao: String,
val valor: Double,
val cupomDesconto: String? = null // este sim pode ser nulo
)
Quanto menos tipos nullable no seu codigo, menos verificacoes de nulo voce precisa fazer e menor a chance de NullPointerException em tempo de execucao.
Prefira Imutabilidade com val
Sempre que possivel, use val em vez de var. Propriedades imutaveis sao mais faceis de entender, mais seguras em contextos concorrentes e facilitam o raciocinio sobre o estado do programa.
// Ruim: var quando nao precisa mudar
var nome = "Kotlin Brasil"
// Bom: val quando o valor nao muda
val nome = "Kotlin Brasil"
O mesmo vale para collections. Prefira listOf() e mapOf() (imutaveis) e so use mutableListOf() quando realmente precisar modificar a colecao.
// Bom: colecao imutavel
val linguagens = listOf("Kotlin", "Java", "Scala")
// So quando necessario
val resultados = mutableListOf<String>()
resultados.add("Primeiro resultado")
Sealed Classes para Modelar Estado
Quando voce precisa representar um conjunto finito de estados — como o resultado de uma chamada de rede ou o estado de uma tela — sealed classes sao a escolha ideal.
sealed class UiState<out T> {
object Carregando : UiState<Nothing>()
data class Sucesso<T>(val dados: T) : UiState<T>()
data class Erro(val mensagem: String) : UiState<Nothing>()
}
// Uso no ViewModel
fun buscarUsuarios(): UiState<List<Usuario>> {
return try {
val usuarios = repositorio.buscarTodos()
UiState.Sucesso(usuarios)
} catch (e: Exception) {
UiState.Erro(e.message ?: "Erro desconhecido")
}
}
// Tratamento exaustivo na UI
when (val estado = viewModel.estado) {
is UiState.Carregando -> mostrarLoading()
is UiState.Sucesso -> mostrarLista(estado.dados)
is UiState.Erro -> mostrarErro(estado.mensagem)
}
Essa abordagem e amplamente usada em projetos com MVVM e Jetpack Compose, e deixa o fluxo de dados muito mais previsivel.
Outras Boas Praticas Rapidas
Antes de encerrar, vale listar mais algumas praticas que fazem diferenca no dia a dia:
- Use string templates: prefira
"Ola, $nome"em vez de"Ola, " + nome. - Use named arguments em funcoes com muitos parametros:
criarUsuario(nome = "Ana", idade = 28)e muito mais claro do quecriarUsuario("Ana", 28). - Use default parameters em vez de sobrecargas de funcao:
fun conectar(
host: String,
porta: Int = 8080,
usarSsl: Boolean = true
) { ... }
- Prefira extension functions para adicionar comportamento a classes existentes em vez de criar classes utilitarias com metodos estaticos.
- Escreva testes desde o inicio. Codigo bem escrito e codigo testavel.
Conclusao
Boas praticas nao sao regras rigidas gravadas em pedra — sao diretrizes que evoluem com a comunidade e com a propria linguagem. O mais importante e manter consistencia dentro do projeto e priorizar a legibilidade. Ferramentas como ktlint e detekt ajudam a automatizar parte desse trabalho, mas o bom senso do desenvolvedor continua sendo insubstituivel.
Se voce esta comecando agora com Kotlin, nao tente aplicar todas essas praticas de uma vez. Va incorporando aos poucos, comecando pelas mais basicas como nomenclatura e uso de val, e depois avance para padroes mais sofisticados como sealed classes e scope functions. Com o tempo, escrever codigo Kotlin idiomatico vai se tornar natural.