5 dicas e um bônus para deixar seu código Kotlin melhor

lissatransborda

Lissa Ferreira

Posted on November 21, 2021

5 dicas e um bônus para deixar seu código Kotlin melhor

Kotlinautas

Esse conteúdo é oferecido e distribuído pela comunidade Kotlinautas, uma comunidade brasileira que busca oferecer conteúdo gratuito sobre a linguagem Kotlin em um espaço plural.

capa Kotlinautas

Créditos

Esse artigo foi baseado principalmente nesses slides do Anton Arhipov.

O quê é Kotlin Ideomático?

Kotlin Ideomático é uma maneira de escrever Kotlin tentando seguir os melhores padrões e funcionalidades da linguagem. Buscando tornar o código mais legível e menor.

Há uma página oficial da documentação do Kotlin apenas para listar diversas formas ideomáticas de fazer coisas. Caso saiba um pouco de inglês, é interessante ler esse documento. Neste artigo vamos abordar apenas algumas formas ideomáticas de se usar Kotlin, mas nessa página há uma quantidade bem maior.

Criando funções sem classes

Muitas pessoas que migram do Java para o Kotlin, continuam com o costume de criar uma classe toda vez que precisarem criar uma ou mais funções, mas isso não é necessário no Kotlin, pois podemos criar funções sem precisar de uma classe associada, dessa maneira:

fun main(){
  olaMundo()
}

fun olaMundo(){
  println("Olá Mundo!")
}
Enter fullscreen mode Exit fullscreen mode
  • Criamos uma função olaMundo, que mostra o texto Olá Mundo! na tela;
  • Chamamos essa função dentro da main;

Extensões

Sabe aquelas funções .toInt, .equals,etc. que podemos usar em tipos do Kotlin? Podemos adicionar novas funções completamente feitas por nós á esses tipos, usando as extensões.

Vamos supor que você quer criar uma função chamada ÉNumero que irá checar se uma string é um número ou não. Essa função seria executada da seguinte forma:

"isso não é um número".ÉNumero
"10".ÉNumero
Enter fullscreen mode Exit fullscreen mode

Isso pode ser feito criando a função ÉNumero dessa maneira:

fun String.ÉNumero() = all { it.isDigit() }
Enter fullscreen mode Exit fullscreen mode
  • Passamos o tipo, no caso String, seguido de um . e o nome da função, que no caso é ÉNumero;
  • A função all irá testar se todos os caracteres do texto conseguem passar de uma certa condição, que no caso é se esse caractere é um dígito usando a função isDigit;

Após isso, podemos utilizar essa função em qualquer lugar do nosso código:

fun main(){
    println("10".ÉNumero())
    println("abracadabra".ÉNumero())
}
Enter fullscreen mode Exit fullscreen mode

Funções de escopo

Funções de escopo são funções axuliares que executam um código com o contexto de um objeto. Isso significa que essas funções criam um escopo temporário, onde poderemos usar todas as propriedades de um objeto sem precisar informar esse projeto. Caso tenha tido alguma confusão, vamos ver na prática.

Vamos supor que temos uma classe Estrela, com as propriedades nome, diâmetro, dessa maneira:

data class Estrela(
    var nome: String = "",
    var diâmetro: Int = 0,
    var numeroDePlanetas: Int = 0,
)
Enter fullscreen mode Exit fullscreen mode

Podemos criar uma variável que guarde uma instância, e mudar as propriedades dessa variável. Dessa maneira:

val sol = Estrela()
sol.nome = "sol"
sol.diâmetro = 1392700
sol.numeroDePlanetas = 8
Enter fullscreen mode Exit fullscreen mode

Esse código irá funcionar, mas não está sendo ideomático, pois estamos repetindo a variável sol diversas vezes. Podemos escrever esse código de uma maneira mais bonita como no exemplo abaixo:

    val sol = Estrela().apply {
        nome = "sol"
        diâmetro = 1392700
        numeroDePlanetas = 8
    }
Enter fullscreen mode Exit fullscreen mode
  • Usamos a função apply do Kotlin, que irá receber um objeto (no caso uma instância de Estrela) e irá fazer alterações com esse objeto em seu contexto. No caso, definindo valores de propriedades.

Dessa maneira, nosso código estará mais consiso, legível, e ideomático.

Há outras funções de escopo, essas outras funções podem ser vistas Nessa página da documentação do Kotlin

Argumentos Padrão e Argumentos Nomeados

Vamos supor que temos a seguinte função:

fun saudações(nome: String, momento: String){
    println("Olá $nome! $momento")
}
Enter fullscreen mode Exit fullscreen mode

Essa função está recebendo um nome, como Pedro ou Maria, e momento recebe uma saudação do dia, como boa tarde ou boa noite.

Mas vamos pensar: Na maior parte das vezes, damos bom dia para as pessoas, e menos vezes damos boa tarde ou boa noite.

Logo, podemos mudar a função e fazer que esse momento tenha um valor padrão usando um = depois de seu tipo.

fun saudações(nome: String, momento: String = "Bom Dia!"){
    println("Olá $nome! $momento")
}
Enter fullscreen mode Exit fullscreen mode

E além, e se essa pessoa não quiser passar seu nome? Podemos deixar Pessoa Anônima como valor padrão, da mesma forma que dizemos com momento. Dessa maneira:

fun saudações(nome: String = "Pessoa Anônima", momento: String = "Bom Dia!"){
    println("Olá $nome! $momento")
}
Enter fullscreen mode Exit fullscreen mode

Com isso, podemos chamar essa função saudações de algumas formas:

fun main(){
    saudações()
    saudações("Maria")
    saudações("Pedro", "Boa Noite!")
}
Enter fullscreen mode Exit fullscreen mode

Resultado:

Olá Pessoa Anônima! Bom Dia!
Olá Maria! Bom Dia!
Olá Pedro! Boa Noite!
Enter fullscreen mode Exit fullscreen mode

E se eu quiser deixar o argumento nome padrão, e mudar só a mensagem?

Isso pode ser feito nomeando o argumento momento na chamada, dessa maneira:

fun main(){
    saudações(momento = "Boa Tarde!")
}
Enter fullscreen mode Exit fullscreen mode

Resultado:

Olá Pessoa Anônima! Boa Tarde!
Enter fullscreen mode Exit fullscreen mode

Funções de única expressão

Vamos supor que temos uma função testarTemperatura que recebe uma temperatura em String e diz se é possível tomar uma bebida nessa temperatura ou não:

fun testarTemperatura(temperatura: String): String{
    when(temperatura){
        "quente" -> return "Quente demais para beber"
        "frio" ->  return "Temperatura boa para beber"
        else -> return "Preciso deixar um pouco na geladeira"
    }
}
Enter fullscreen mode Exit fullscreen mode

Essa função tem apenas uma única expressão, um único comando em seu corpo. logo, podemos simplificar essa função, removendo seu corpo e seu retorno e adicionando um = seguido pela expressão.

Dessa maneira, nossa função ficará assim:

fun testarTemperatura(temperatura: String) = when(temperatura){
        "quente" -> "Quente demais para beber"
        "frio" ->  "Temperatura boa para beber"
        else -> "Preciso deixar um pouco na geladeira"
}
Enter fullscreen mode Exit fullscreen mode

Dessa maneira, nossa função ficará bem menor, consisa e legível, essas são as funções de única expressão, a mesma coisa que fizemos com o when pode ser feita com if, try,etc.

Operador Elvis

Vamos supor que temos uma classe Cachorro, essa toda instância de Cachorro tem um nome, raça (que pode ser null caso esse cachorro seja vira-lata) e uma propriedade educado. Dessa maneira:

class Cachorro(
    val nome: String,
    val raça: String?,
    val educado: Boolean,
)
Enter fullscreen mode Exit fullscreen mode

Junto com essa classe, temos uma função apresentarCachorro que irá receber uma instância dessa classe Cachorro, e caso esse cachorro seja de raça, e a sua raça não seja null, o seu nome e raça serão mostrados na tela jutn ocom uma mensagem. Normalmente, fariamos dessa maneira:

fun apresentarCachorro(cachorro: Cachorro){
    if (cachorro.raça == null){
        return
    }

    val raça = cachorro.raça
    val nome = cachorro.nome

    if (cachorro.educado){
        println("Au Au Au, meu nome é $nome e sou da raça $raça, Au Au Au")
    }
}
Enter fullscreen mode Exit fullscreen mode

Esse código pode ser melhorado. Podemos remover o primeiro if que testa se a raça do cachorro é null ou não. Podemos colocar esse teste direto na definição da variável raça, dessa maneira:

fun apresentarCachorro(cachorro: Cachorro){
    val raça = cachorro.raça ?: return
    val nome = cachorro.nome

    if (cachorro.educado){
        println("Au Au Au, meu nome é $nome e sou da raça $raça, Au Au Au")
    }
}
Enter fullscreen mode Exit fullscreen mode

No código acima, usamos o operador Elvis (?:), onde se o valor da esquerda for null, o código da direita será executado. No caso, é o return que irá fechar a função sem nenhum conteúdo no retorno.

Finalização

Nesse artigo, você aprendeu 6 dicas de como deixar o seu código Kotlin mais ideomático, consiso e legível. Busque aplicar os conceitos mostrados nesse artigo (e em outros sobre Kotlin Ideomático que podem existir) em seus projetos, pois assim seu código ficará bem melhor.

Muito obrigada por ler ❤️🏳️‍⚧️ e me segue nas redes, é tudo @lissatransborda 👀

💖 💪 🙅 🚩
lissatransborda
Lissa Ferreira

Posted on November 21, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related