Motivos para trocar o Java pelo Kotlin ainda hoje

Bernardo do Amaral Teodosio
Movile Tech
Published in
9 min readJan 15, 2018

--

Em meados de 2016 nós, como desenvolvedores Android, já estávamos de olho na linguagem Kotlin. Na Movile, temos a inovação no nosso DNA, e assim que surgiu a oportunidade de começar a usar a nova linguagem, resolvemos fazê-lo.

Se você ainda não conhece Kotlin, uma breve descrição que costumo usar quando falo do assunto é:

Uma linguagem de programação de tipagem estática, concisa, criada pela JetBrains e que está em constante desenvolvimento. É totalmente interoperável com Java e permite também a criação de aplicações web e nativas.

A linguagem Kotlin foi criada pela JetBrains, que é a mesma empresa que criou as melhores IDEs existentes atualmente no mercado, como: PHPStorm, CLion, PyCharm e IntelliJ, que é também a base do Android Studio, IDE oficial da Google para desenvolvimento de aplicações Android.

Interoperabilidade com Java

O Kotlin ser 100% interoperável com Java significa que você pode começar a usá-lo assim que quiser, em um projeto Java já existente (seja ele um servidor ou um aplicativo Android), sem a necessidade de migrar nenhum código, e sem precisar esperar a oportunidade de um novo projeto para começar a utilizar a linguagem. Você pode inserir o código Kotlin em um projeto já existente hoje, chamá-lo a partir do código Java, e vice-versa, e não terá nenhum problema com isso. Este é mais um motivo para dar uma chance para a linguagem.

Oficialmente suportada pelo Google

Em 2016, quando começamos a utilizar Kotlin em nossos projetos, a Google ainda não havia se pronunciado oficialmente sobre o uso da linguagem para o desenvolvimento Android. Porém, como temos a cultura de testar e aprender rápido, este silêncio não foi um impeditivo — fato pelo qual temos orgulho até hoje.

Desde o anúncio oficial da Google sobre o suporte da linguagem Kotlin para o desenvolvimento Android, no Google I/O 2017, cada vez mais cresce o número de aplicações Android escritas parcial ou totalmente em Kotlin, o que significa que cada vez mais desenvolvedores no mercado estão fazendo o uso da mesma.

A partir do Android Studio 3.0, o suporte à linguagem Kotlin foi adicionado nativamente na IDE, facilitando ainda mais o desenvolvimento de aplicações com o uso da mesma.

Sintaxe simples, concisa e pouco verbosa

Kotlin possui uma sintaxe simples e concisa. Códigos escritos nesta linguagem possuem legibilidade melhor do que códigos equivalentes escritos em Java. Veja abaixo três formas diferentes de escrever uma simples função que realiza a soma de dois números.

Os 2 primeiros exemplos estão escritos em Kotlin, enquanto que o terceiro está escrito em Java. Podemos notar que o código Kotlin, além de possuir maior legibilidade, é também muito menos verboso. Não precisamos criar uma classe para fazer uma simples função, como em Java. Além disso, não precisamos dizer que os parâmetros são final em Kotlin. Eles já o são, por padrão — o que é um outro ponto muito positivo da linguagem, que incentiva a imutabilidade.

Ah, e outra maravilha: ponto-e-vírgula é opcional!

Como exposto nos exemplos acima, o construtor da classe Java, que não faz nada além de inicializar os atributos da classe com os valores dos parâmetros fornecidos, foi reescrito em Kotlin de forma muito mais simples, logo após o nome da classe. Tudo o que está entre parênteses após o nome da classe Kotlin são propriedades da classe, inicializadas a partir de parâmetros do construtor primário da mesma.

Data classes

Em algum momento você, com toda certeza, já teve que fazer classes que servem simplesmente para representar um conjunto de dados na forma de um modelo, e nada mais. São as famosas classes Models / POJOs. Geralmente tais classes não possuem nada de especial, apenas alguns atributos: um construtor que os inicializa, métodos getters e setters e, algumas vezes, sobrescrevem os métodos equals(), hashcode() e toString().

Apesar de simples, em Java as mesmas tendem a ficar muito grandes por conta da verbosidade da linguagem. Abaixo temos um exemplo de uma classe que representa um usuário em Java:

Kotlin possui um tipo especial de classes que pode ser utilizado especialmente para estes casos. São as chamadas data classes. Utilizando esta funcionalidade, o código equivalente em Kotlin pode ser visto abaixo:

Como podemos perceber, a classe escrita em Kotlin é muito mais simples, mais rápida de escrever e também mais simples de ler, uma vez que não é recheada de código desnecessário que atrapalha a leitura. Por padrão, data classes já possuem uma implementação útil de equals(), hashCode() e toString(), que usa como parâmetros as propriedades definidas no construtor da classe e, por isso não é necessário re-implementar estes métodos. Além disso, data classes também possuem um método copy(), que serve para criar cópias de uma instância alterando apenas determinados atributos. Suponha, por exemplo, que você queira criar um novo usuário, com os mesmos atributos de um usuário já existente, mas alterando apenas a idade. É simples fazer isto utilizando este método:

Null safety

Se você é um programador Java, já deve ter visto a seguinte linha em seu monitor:

java.lang.NullPointerException

A tão famosa e temida NullPointerException, que ocorre quando tentamos chamar um método, acessar um atributo ou fazer algo do tipo em uma instância nula, isto é, usando uma referência que não aponta para lugar algum. É, provavelmente, a exceção mais comum — e também a mais irritante — quando se trata do Java. E, pior ainda, ela ocorre em tempo de execução — o que significa que a execução da sua aplicação pode ser interrompida quando esta exceção é lançada, prejudicando fortemente os usuários da mesma.

Com o Java 8, veio à linguagem o conceito de Optionals, que ajudam a evitar bastante o número de ocorrências desta exceção. Porém, se você é um desenvolvedor Android, sabe o quão difícil é utilizar funcionalidades de versões mais novas do Java nos aplicativos. Por conta das limitações das versões do sistema, para ser possível utilizar novas funcionalidades do Java em versões mais antigas do Android, precisamos procurar alternativas de compatibilidade, tais como Retrolambda.

Utilizando Kotlin, não precisamos nos preocupar com isso. A linguagem diferencia referências que podem ser nulas daquelas que não podem ser, de acordo com o tipo definido. Se você define um tipo como sendo “não-nulo”, então a linguagem garante, em tempo de compilação, que valores nulos não serão atribuídos à referência do tipo não nulo.

No primeiro exemplo, temos uma referência do tipo String, explicitamente declarado. Esta referência não pode receber valores nulos.

No segundo exemplo, temos uma referência do tipo String, também explicitamente declarado. Ao tentar atribuir o valor null para esta referência, obtemos um erro de compilação, uma vez que, a referência é do tipo String e não String? (a presença da interrogação indica um tipo que pode receber valores nulos).

No terceiro exemplo, temos uma referência do tipo String?, nullable, que pode receber valores nulos.

No quarto exemplo, temos uma referência do tipo String, não declarado explicitamente, mas que é inferido pelo compilador a partir do valor atribuído à variável.

Nos testes acima, vemos alguns exemplos da funcionalidade em uso. Temos inicialmente uma função de exemplo, getLengthPlusThree(), que retorna o tamanho de uma string (recebida por parâmetro) mais três. Observe que a string recebida por parâmetro é não nula, o que significa que é impossível chamar esta função passando uma string null como parâmetro.

No primeiro teste, tentamos chamar a função passando diretamente uma string null como parâmetro. Como esperado, recebemos um erro em tempo de compilação.

No segundo teste, tentamos chamar a função passando uma string nullable como parâmetro. Novamente, temos um erro de compilação. Embora não saibamos qual é o valor do parâmetro anotherString, sabemos que ele pode ser nulo. Por conta desta possibilidade, a função getLengthPlusThree() não pode ser chamada.

No terceiro teste, utilizamos um if para testar o valor do parâmetro recebido. Após garantir que o parâmetro não é nulo, podemos chamar normalmente a função getLengthPlusThree(), pois não existe mais a possibilidade de que o valor do parâmetro passado seja nulo.

Nestes exemplos, podemos também notar outras duas funcionalidades interessantes da linguagem. A primeira delas é a nomeação de parâmetros. Nos testes 1 e 2, ao chamar a função getLengthPlusThree(), explicitamos o nome do parâmetro string da função na hora de chamá-la. Apesar de ser opcional, esta funcionalidade deixa o código mais legível — principalmente ao chamar funções com parâmetros do tipo boolean. A segunda funcionalidade é o retorno da expressão if. Em Kotlin, podemos utilizar o if (e também outras construções) como retorno de uma função.

Extension functions

As famosas classes Utils, qualquer projeto Java razoavelmente grande contém pelo menos uma. StringUtils, FileUtils, AndroidUtils. É muito provável que você já tenha utilizado ou até mesmo criado alguma, pelo menos uma vez.

Em geral, classes Utils são criadas para agrupar métodos que fazem determinadas operações com objetos de um mesmo tipo. Uma classe FileUtils, por exemplo, estaria repleta de métodos estáticos, que recebem como parâmetro um File, e fazem uma determinada operação com este arquivo. Tais classes, apesar de funcionais (são, de fato, úteis), costumam ser compostas por código verboso, e consequentemente mais trabalhoso para ler e escrever.

Kotlin possui uma funcionalidade que resolve o problema das classes Utils e deixa o código mais simples, legível e bonito. Além disso, facilita também a manutenção do mesmo: as Extension Functions. Basicamente, Kotlin provê a capacidade de adicionar funcionalidades em uma classe já existente, sem precisar herdá-la para isto.

No exemplo acima, criamos uma função que estende a classe String. Agora, podemos chamar esta função em qualquer string, como no exemplo abaixo:

Se a função for utilizada em um arquivo diferente daquele em que foi declarada, é necessário importá-la.

Considere agora o exemplo abaixo, no qual foi criada uma Extension Function para calcular o tamanho em megabytes de um arquivo. Ao invés de criar uma classe FileUtils para fazer isto, podemos escrever no formato abaixo, em Kotlin:

A palavra chave vararg indica que o número de arquivos que o método receberá como parâmetro é indefinido (número variável de argumentos).

Se você desenvolve Android, provavelmente já utilizou Glide ou Picasso alguma vez, famosas bibliotecas para o carregamento de imagens. Podemos utilizar Extension Functions para criar uma abstração em volta destas bibliotecas que, além de tornar o código menor e muito mais legível, facilita também uma eventual substituição de bibliotecas. O exemplo abaixo ilustra esta situação:

Higher order functions e streams

O Kotlin suporta funções de alta ordem. É possível utilizar funções como tipos e parâmetros, e até mesmo retornar funções a partir de uma função. Com esta funcionalidade, fica aberto um enorme leque de possibilidades para programação funcional utilizando Kotlin. Mas, não apenas por isto, é possível criar um código muito mais limpo, conciso e simples, utilizando funções de alta ordem do que em Java, que só introduziu (parcialmente) esta funcionalidade a partir do Java 8.

Se você usa RxJava para desenvolvimento Android, provavelmente usa também alguma biblioteca adicional para utilizar lambdas, já que esta funcionalidade foi introduzida apenas no Java 8, e é fundamental para o funcionamento do RxJava. Utilizando Kotlin, nenhuma biblioteca adicional será necessária, já que a linguagem suporta nativamente esta funcionalidade.

Além disso, a linguagem também dispões de streams e operadores para streams, funcionalidades extremamente úteis e que também só foram introduzidas no Java 8. Veja abaixo um exemplo de código Kotlin que utiliza streams e funções de alta ordem:

A função testStreams() constrói uma lista de itens de um pedido, filtra os mesmos baseados em determinados critérios e depois envia o pedido, em uma nova thread, invocando a função fetchOrder(). Quando esta é completada, a função invoca orderCallback(), o callback com a resposta do pedido. Note que orderCallback() é uma função que foi passada como parâmetro para a função inicial testStreams().

Dê uma chance

Além de todos estes motivos e, além da sua produtividade aumentar muito, é divertido programar em Kotlin. É divertido não ter que escrever um monte de boilerplate para fazer algo simples, descobrir um jeito novo de programar e não ficar preso a determinada versão da linguagem deixando de experimentar novas funcionalidades por conta disso. O mundo gira, o tempo passa, e nós não somos obrigados a continuar no passado. Dê uma chance para algo novo e experimente o Kotlin.

Neste e neste link, podem ser encontradas partes de uma live da qual participei, falando sobre como utilizamos a linguagem na Movile.

Abaixo, uma apresentação que fiz na Kotlin Night Campinas, evento que ocorreu em 2017, no qual tive a oportunidade de falar um pouco sobre a linguagem. O áudio das apresentações do evento pode ser encontrado aqui.

--

--

Bernardo do Amaral Teodosio
Movile Tech

Tech Lead & Mobile Developer @ Sandbox Group/PlayKids | BSc CS (UNICAMP) | MSc Candidate @UNICAMP