11 dicas (não tão) rápidas sobre Kotlin

No items found.
27/9/2019
William Okano
William Okano
Back-end Developer

Apaixonado por desenvolvimento, mas não dispensa comidas deliciosas e oportunidades de dormir.

Está sem tempo para ler? Aperte o play para escutar o artigo.

O objetivo deste artigo é descrever algumas dicas que considero simples e rápidas para usuários Kotlin, independente de qual plataforma você esteja utilizando.

Vamos às dicas:

1. Validação de argumentos

As vezes temos funções que queremos validar nossas entradas. Geralmente escrevemos alguns códigos do tipo

<p> CODE: https://gist.github.com/maluaraujo/82b228e5c561e23d1175cdcc07cbfa00.js</p>

Observe que se neste caso tivéssemos várias validações, repetiríamos várias vezes if (...) e o throw new IllegalArgumentException("...").

Uma forma que Kotlin nos provê nativamente para essas tratativas são as funções require e check. A diferença básica entre as duas é que require deve ser utilizada para validar se uma entrada é válida ou não, lançando sempre a exceção IllegalArgumentException, enquanto o check lança exceção IllegalStateException e deve ser utilizado para validações de estados, como por exemplo, validar se a idade de uma pessoa condiz com o fato de ela poder beber. 

Veja o exemplo:

<p> CODE: https://gist.github.com/maluaraujo/21cc6c440a01cd74e4e00eb400755947.js</p>

2. Sealed Class

Sealed classes, ou classes seladas, são classes que representam uma hierarquia de classes muito restrita, por exemplo quando você tem um conjunto muito restrito de tipos. 

Uma vantagem de se utilizar classes seladas é que além de proibir desenvolvedores de criarem novas subclasses, quando utilizada com o pattern matching when em forma de expressão do Kotlin, ele precisa ser exaustivo, sendo que como é um subconjunto muito bem definido, não existe a necessidade do else, sendo necessário implementar todos os casos.

Alguns usos comuns de sealed classes em Kotlin podem ser feitos ao executar chamadas Http, onde, em teoria, você poderia retornar 3 tipos de valores: HttpSuccess para retornos da família 2xx, ClientError para retornos da família 4xx, ServerError para retornos da família 5xx, etc.

Outro uso que pode ser destacado seria quando se utilizado com alguns conceitos de programação funcional, onde podemos encapsular uma chamada de função em um Success ou um Error

Por exemplo:

<p> CODE: https://gist.github.com/maluaraujo/e5a4d3ef1dc441626a5d41bac4d9d106.js</p>

3. Lazy initialization

As vezes queremos executar alguma ação em nosso código, mas essa ação pode ser demorada e gostaríamos de postergar isso o máximo possível. 

Uma forma de evitar essa chamada é utilizando o recurso de lazy initialization (inicialização preguiçosa). Vamos supor que tenham duas classes, LazyHolder e VeryHeavyClass, onde eu precise criar uma instância de LazyHolder, mas quero evitar a execução de VeryHeavyClass até algum momento que seja mais propício. 

Um exemplo prático poderia ser na construção de um framework, onde o container de injeção de dependência não criasse as dependências na inicialização da aplicação, fazendo ela subir mais rápido, mas apenas no ato do uso, fazendo a primeira chamada ser um pouco mais lenta, mas com uma carga que poderia ser dividida ao longo do ciclo de vida do projeto. 

Exemplo:

<p> CODE: https://gist.github.com/maluaraujo/062e8ae047af3ead861499269bf46cc1.js</p>

Como você pode ver, ambos terminam ao mesmo tempo, entretanto o `Ready to action` do block Lazy acontece um pouco antes.

4. Destructuring

Em Kotlin, assim como em algumas outras linguagens, como Javascript, é possível atribuir valores de variáveis a partir de um objeto, utilizando **desestruturação**. Um exemplo de uso seria para facilitar um pouco a leitura quando fazemos uso intensivo o objeto em questão. 

Observe os 2 métodos, imprimirPessoa e imprimirPessoaDesestruturacao para ver como o print ficou um pouco mais limpo e legível.


<p> CODE: https://gist.github.com/maluaraujo/8598a3c2bb3a08ad2052b087bd50c265.js</p>

5. Operator overloading (Sobrecarga de operador)

Já trabalhou com BigInteger ou BigDecimal em Java e achou super fora de mão ter que fazer valor1.add(valor2), ou algo do tipo. 

Em Kotlin, suas classes podem ter os operadores sobrecarregados, ou seja, você pode customizar o comportamento de alguns operadores. Um exemplo com a classe Moeda:

<p> CODE: https://gist.github.com/maluaraujo/cca368a1a7339dd2401eccd0d466954e.js</p>

Para saber sobre todos os operadores que podem ser sobrecarregados, você pode conferir neste link da documentação oficial.

6. Property delegation

Vamos supor que você queira mudar o comportamento padrão de como o getter e o setter de uma classe funciona em Kotlin, você poderia fazer algo do tipo:

<p> CODE: https://gist.github.com/maluaraujo/3c50af70243f69767e87e13183de1e39.js</p>

Agora imagine que você tenha isso para muitos campos, sua classe ficaria com centenas de linhas de códigos, dependendo de como sua propriedade funcionasse. Ou até milhares. Isso tudo pode ser minimizado se você tiver uma classe que fique responsável apenas por definir como funcionam as regras de get e set. 

Essa classe deve ter os operadores getValue e setValue definidos. 

Veja o exemplo:

<p> CODE: https://gist.github.com/maluaraujo/8e3a77fa3591f6399a9713c974cc8cbc.js</p>

Bem mais enxuta a classe ExemploDelegate, não?

7. Sequences

As vezes queremos trabalhar com listas, e não nos atentamos para a performance da execução de nossos métodos. 

Ignorando que já temos vários métodos built-ins, vamos supor o seguinte cenário: temos uma lista de inteiros, na qual eu quero executar uma operação de mapeamento para todos os valores, mas essa operação é pesada. Também queremos fazer uma operação de filtro, que também pode ser muito pesada. 

Por fim, do resultado, gostaria de pegar apenas os n primeiros elementos, e essa operação seria até bem rápida.  

Na prática, precisaríamos executar o mapeamento apenas nos n primeiros elementos do qual o filtro for bem sucedido. Assumindo que cada operação demore 100 milissegundos, se tivéssemos uma lista com 1_000_000 de itens, e quiséssemos executar essa ação, teríamos um mapeamento de 1_000_000 de elementos, um filtro sobre 1_000_000 de elementos para no final pegarmos apenas os n primeiros.  

Nosso consumo de memória seria 1_000_000 da lista original, mais 1_000_000 de itens do mapeamento (que foi lento), até 1_000_000 elementos resultantes do filter e depois outra lista com n elementos que queremos de fato. Isso claro, no modelo tradicional.

Utilizando sequences do Kotlin, podemos executar muito menos operações, pois uma sequence decora uma função que será executada item a item, ou seja, a cada item, eu executo todas as condições, e se no final tudo for verdadeiro, eu paro a execução. 

Se utilizamos o valor 5 para n no exemplo acima, teríamos em torno de 10 mapeamentos, 5 filtros e nenhuma lista intermediária para armazenar este valor. 

Exemplo:

<p> CODE: https://gist.github.com/maluaraujo/9b31768393e38d565701c329f0d00a59.js</p>

Observe que utilizando sequence foi bem mais rápido (e também gastou menos memória, mas não quis entrar neste detalhe para expor o consumo real de RAM). Veja as saídas:

<p> CODE: https://gist.github.com/maluaraujo/9c04840438caffe27f6e4d29e355a7cb.js</p>

Caso queira entender mais a fundo, e com uma ajudinha visual, veja aqui.

Se quiser um artigo um pouco mais profundo, veja aqui da própria Kotlin Academy.

webinar microsserviços e overengineering

8. Executando ação se variável não for nula

Essa dica é bem rápida mesmo. Muitas vezes se uma variável for nula, então geralmente fazemos algo do tipo:

<p> CODE: https://gist.github.com/maluaraujo/9302d0ff6214054dc069c5b1c79f3394.js</p>

Mas em Kotlin, você pode combinar o operador de nullsafe `?` e o operador de escopo `let` para executar um código apenas se o valor não for null. A leitura não é a melhor do mundo (até você se acostumar), mas é bem prático. 

Exemplo:

<p> CODE: https://gist.github.com/maluaraujo/789d8884e734ece298a0cf1e9c0190be.js</p>

9. Espaços em nomes de funções

Tá, vou confessar que usar essa em regras de domínio é pedir para ter problemas no futuro, mas sabe quando você está escrevendo aquele teste maroto e quer colocar o nome do método super descritivo? Então, em Java você faria algo do tipo:

`deve_salvar_no_banco_e_depois_publicar_em_uma_fila_kafka`. 

Certo? Bom, em Kotlin você não precisa do underscore, pode usar espaço direto se o nome do seu método estiver dentro de crases. 

Como por exemplo:

<p> CODE: https://gist.github.com/maluaraujo/c5ff85a1f998f76cb4c47d8f0ec015e0.js</p>

Ok, não é sensacional, mas ajuda bastante na leitura do nome dos testes.

10. Infix functions

Infix functions são legais, mas o uso dela é mais para gerar códigos mais legíveis do que algo realmente prático. Mas diferente do nome com espaços, fica até legal utilizar nos códigos de domínio. Vamos direto ao exemplo:

<p> CODE: https://gist.github.com/maluaraujo/2cbe809b1d188f3a089ec349557c348d.js</p>

11. Local functions

Sabe aquelas vezes que você quer refatorar o código pois está muito grande mas você não acha que uma classe deveria ser exposta (é parte única e exclusivamente do meu método) e mesmo assim você quer separá-la do resto? 

Você pode criar funções dentro de funções, assim manterá suas funções bem concisas e não expõe API's desnecessárias pra fora do contexto necessário. Pois é, loucura não?

Olha só:

<p> CODE: https://gist.github.com/maluaraujo/ea5e2ab39a6fcaacd97812cdffa915dd.js</p>

kotlin

Nossas dicas foram úteis pra você? Conta pra gente nos comentários! 

Aproveita para compartilhar o artigo com alguém que precisa muito aprender sobre o assunto. 

O que você achou deste conteúdo?
Quer receber nossos conteúdos?
Seu cadastro foi efetuado com sucesso! Enviaremos as novidades no seu email.
Oops! Something went wrong while submitting the form.