No dia 17 de Maio, durante o keynote do I/O 2017 – o maior evento onde a Google apresenta as novidades dos seus produtos, plataformas e serviços – foi anunciada uma excelente surpresa para nós androiders: Kotlin será oficialmente a nova linguagem de desenvolvimento Android.
Mas porque a Google adotou essa estratégia com uma linguagem que tem apenas 6 anos de idade em detrimento de Java, que existe há várias gerações? Ainda vai ser possível desenvolver apps Android em Java? Qual explicação para essa meteórica ascensão de Kotlin?
Esse post pretende responder à essas questões. Ele será composto de várias partes. Na Parte I, você irá aprender um pouco mais sobre Kotlin, noções básicas e principais features que se destacam em relação à Java. Na parte II, você irá aprender a configurar e construir o seu primeiro projeto em Kotlin utilizando o Android Studio 3 Preview que já vem com suporte à nova linguagem. Como exemplo prático, será criado o projeto “Kiphy“, uma app simples que lista GIFs animados utilizando a Giphy API. Você ainda poderá compartilhar com seus amigos 🙂
A long time ago, in a galaxy far far away…
Existia uma linguagem chamada Java, que foi concebida com o objetivo de ser totalmente independente da plataforma e que, executasse em qualquer lugar, desde anéis inteligentes até contâiners Web. Essa independência de plataforma seria possível graças à famosa Java Virtual Machine (ou apenas JVM), que compilava código-fonte para bytecodes que seriam interpretados em tempo de execução. Mas o que tudo isso teria a ver com Kotlin? Na verdade, tudo!
O despertar de Kotlin
Criada em 2011 pela empresa JetBrains (a mesma que criou a IDE IntelliJ, alicerce para o Android Studio), os engenheiros sentiram a necessidade de criar uma linguagem para desenvolvimento que fosse: concisa, fácil de aprender, intuitiva, que rodasse em bytecodes da JVM. Tão logo a linguagem fora anunciada naquele ano, já tinham vários early adopters experimentando a linguagem em seus projetos, chegando há um pouco mais de milhares linhas de códigos.
Em Fevereiro do ano passado, a versão 1.0 foi anunciada publicamente e não tardou para alcançar milhares de linhas de código no GitHub em projetos open source. Depois de um ano, a JetBrains lançou a versão 1.1 da linguagem e publicou esse link, mostrando que já haviam sido alcançadas 10 milhões de linhas de códigos em 8132 repositórios no GitHub.
Todo mundo tava falando de Kotlin! Todo dia saíam posts em blogs, palestras, tutoriais, inclusive como utilizar para construção de apps em Android. A propósito, algumas empresas internacionais como Pinterest, Coursera, Netflix, Uber, Square, Trello e, Basecamp; e as brazucas 99 Taxis e Nubank, já utilizam a linguagem em seus apps. Em produção! Mas qual o principal motivo para Kotlin se tornar um fenômeno?
Por que Kotlin é tão produtivo?
Aqui vão algumas características que tornam Kotlin bem mais produtivo do que Java:
- 100% de interoperabilidade com Java: Como Kotlin converte para bytecodes para rodar em uma JVM, é permitido chamar código Java na sintaxe de Kotlin e vice-versa. Kotlin também é compatível com JavaScript.
- Fácil e rápida de aprender: Principalmente se você já possuiu um pouco de experiência com Java, as features da linguagem podem ser aprendidas de semanas à poucos meses.
- Concisa: Kotlin é bem concisa e consegue fazer muito com menos código, pois possui várias convenções e boas práticas que já vem por padrão integradas na linguagem.
- Expressiva: Kotlin é muito expressiva. Assim, é fácil criar uma DSL (Domain Specific Language) que facilita ainda mais a legibilidade do seu código. Ler um trecho em Kotlin é muito próximo à linguagem natural, o que facilita ainda mais a adoção da linguagem.
O que tem de legal em Kotlin?
Na seção a seguir, serão mostradas as principais características de Kotlin que trazem um ganho de produtividade muito maior no desenvolvimento.
Data class
Em Kotlin, uma data class é uma classe específica para representar entidades. Porém, com menos burocracia que os POJOs (Plain Old Java Objects) do Java. Um simples POJO Person.java, ficaria assim:
[code language=\\\”java\\\” title=\\\”Person.java\\\”]
public class Person {
private Integer id;
private String name;
private Date birthDate;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthDate() {
return birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (id != null ? !id.equals(person.id) : person.id != null) return false;
if (name != null ? !name.equals(person.name) : person.name != null) return false;
return birthDate != null ? birthDate.equals(person.birthDate) : person.birthDate == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (birthDate != null ? birthDate.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name=\\\’" + name + \\\’\\\\\\\’\\\’ +
", birthDate=" + birthDate +
"}";
}
}
[/code]
E em Kotlin, ficaria apenas assim:
[code language=\\\”javascript\\\” title=\\\”Person.kt\\\”]
data class Person (val id: Int, val name: String, val birthDate: String){}
[/code]
A diferença do boilerplate code necessário em Java é significativo. O código todo alcança facilmente mais de 50 linhas de código, com apenas três atributos na classe. Apesar do Android Studio fornecer geração automática para esses métodos, de qualquer maneira a cada adição de um atributo, será necessário regerar os métodos toString(), equals(), hashCode(). E por que em Kotlin isso não é necessário? Pois para data classes, a linguagem já adiciona esses métodos em tempo de compilação.
Properties
Kotlin também possui o conceito de Properties, assim como C#, uma de suas linguagens ancestrais. Dessa forma, não é mais necessário utilizar métodos acessores (getters) e nem modificadores (setters). Por exemplo, se você quiser acessar o valor de um atributo em Kotlin, basta fazer:
[code language=\\\”javascript\\\”]
var person = Person() // construindo o objeto Person (sem “new")
println(“Name: ${person.name}”) // referenciando valores dentro de Strings
[/code]
E para atribuir valores, basta fazer:
[code language=\\\”javascript\\\”]
person.name = “Ramon Rabello”
[/code]
Repare que não foi preciso declarar o tipo do objeto, apesar de ser aceito. Isso porque Kotlin irá inferir o tipo do objeto assim que a variável for referenciada. Tanto as classes quanto as properties tem visibilidade public por padrão. Porém, se desejar encapsulamento, é permitida a utilização das palavras private ou protected. As propriedades podem ser mutáveis (declarando com var) ou imutáveis (declarando com val).
Construtores
Lembra que eu falei que Kotlin é bem conciso? É possível perceber claramente isso no código anterior:
[code language=\\\”javascript\\\”]
data class Person (val id: Int, val name: String, val birthDate: String){}
[/code]
No cabeçalho ocorre tanto a declaração da data class quanto a do construtor primário. Vale lembrar que, a utilização dessa sintaxe é possível apenas se o construtor não tiver nenhum modificador de visibilidade ou anotações. Dessa forma, se eu quiser instanciar um objeto Person, basta executar esse trecho:
[code language=\\\”javascript\\\”]
var person = Person(1, ”Ramon Rabello”, “21/03/1986“)
[/code]
E se eu quiser adicionar construtores secundários? Basta adicionar novos métodos com a seguinte sintaxe:
[code language=\\\”javascript\\\”]
data class Person(val id: Int) {
constructor(id: Int, name:String) : this(id) {}
constructor(id: Int, name:String, birthDate: String) : this(id, name) {}
}
[/code]
Functions
Em Kotlin temos o conceito de funções, que podem ser simples ou mais complexas, como as high-order functions, assim como em linguagens funcionais. Isso significa que é possível passar função como parâmetro para outra função, por exemplo. O poder disso é magnífico! Mas vamos deixar essa explicação para um outro artigo sobre Kotlin. Para declarar uma função, basta utilizar a palavra reservada fun antes do seu nome. A sintaxe fica assim:
[code language=\\\”javascript\\\”]
fun printPerson(val person:Person) {
println(“Person=$person“)
}
[/code]
Se for uma sobrescrita, basta adicionar a palavra override antes de fun. A sintaxe fica dessa forma:
[code language=\\\”javascript\\\”]
override fun printPerson(person:Person) {
println(“Person=${person}“)
}
[/code]
Null Safety: Bye bye, NullPointerException!
O que acontece se você executa um código Java e tenta acessar a variável que está nula? Claro, o aterrorizante NullPointerException, pesadelo de todo desenvolvedor Java. Bom, em Kotlin isso não é necessário pois a linguagem possui o conceito de Null-Safety, isto é, o código só será executado se um objeto não for nulo. O trecho a seguir só será executado se a String não for nula:
[code language=\\\”javascript\\\”]
fun bindPerson(person:Person?) {
textView.text = person?.name ?: “Unknown name”
}
[/code]
O operador “?” ao lado do tipo do atributo indica que a variável do tipo Person pode ser nula (ou nullable em Inglês). Ao fazermos:
[code language=\\\”javascript\\\”]
print(person?.name ?: “Unknown name")
[/code]
Garantimos que este só será executado se o mesmo não for nulo. O operador “?:\\\” é conhecido como Elvis Operator (a interrogação não lembra o topete do cantor?) e é um ternário expressivo que indica o trecho que vai ser executado caso não seja nulo (lado esquerdo da expressão) ou se for nulo (lado direito da expressão).
Extension Functions
Outra grande sacada de Kotlin é permitir estender funcionalidades ou propriedades de classes, sem a necessidade de herdar delas, assim como em C# e Gosu. O poder disso é fantástico! Vamos pensar num cenário bem simples: iniciar uma nova Activity. Para isso, em Java, minimamente precisamos criar uma Intent e passá-la como parâmetro para o método Activity.startActivity(). Como mostra o exemplo abaixo:
[code language=\\\”java\\\” title=\\\”MainActivity.java\\\”]
Intent intent = new Intent(this, DetailsActivity.class);
intent.putExtra("user.id", 135);
intent.putExtra("user.name", "Ramon Rabello");
startActivity(intent);
[/code]
E em Kotlin, como ficaria? Assim:
[code language=\\\”javascript\\\” title=\\\”IntentExtensions.kt\\\” htmlscript=\\\”true\\\”]
fun Activity.start(targetActivity:Class<*>, params:List<Pair<String, String>>) : Unit {
val intent = Intent(this, targetActivity)
for (param in params){
intent.putExtra(param.first, param.second);
}
// intent options and extras go here
this.startActivity(intent)
[/code]
Agora, para chamar uma outra Activity basta usar a extension function que acabamos de criar:
[code language=\\\”javascript\\\”]
var params = listOf("user.id" to "135",
"user.name" to "Ramon Rabello")
activity.start(DetailsActivity::class.java, params)
[/code]
Smartcasts
Em Java, quando temos um trecho de código com instanceof, é necessário o cast manual, da seguinte forma:
[code language=\\\”java\\\”]
public void chooseShape(Shape shape){
if (shape instanceof Rectangle){
Rectangle rectangle = (Rectangle) shape;
// do something with rectangle
} else if (shape instanceof Circle){
Circle circle = (Circle) shape;
// do something with circle
} else if (shape instanceof Triangle){
// do something with triangle
Triangle triangle = (Triangle) shape;
}
}
[/code]
Em Kotlin, temos o conceito de smartcasts, no qual o cast acontece automaticamente inferido a partir do tipo do objeto. Utiliza-se a palavra reservada is. No código abaixo é mostrado um exemplo de smartcast em ação para imprimir valores da subclasse específica de Shape, que pode ser Circle, Rectangle ou Triangle. Repare que é possível fazer a referencia direto à funções ou atributos automaticamente de um Shape, sem a necessidade de cast manual.
[code language=\\\”javascript\\\”]
val shape:Shape = Circle(12.0)
// val shape:Shape = Rectangle(13.0, 16.00) // uncomment to test
// val shape:Shape = Triangle(3.0, 4.0) // uncomment to test
when(shape){
is Circle -> println(“This is a circle of radius ${shape.radius}")
is Rectangle -> println("This is a rectangle of length ${shape.length} and width ${shape.width}")
is Triangle -> println("This is a triangle of area ${shape.area()}”)
}
[/code]
E agora?
E aí, curtiu Kotlin? Bom, essas foram apenas algumas features disponíveis em Kotlin. Se quiser saber mais detalhes sobre outras features, como lambdas, co-routines, high-order functions, companion object, sealed classes, infix e inline functions, dê uma olhada no guide oficial de Kotlin, ou experimente os kotlin-koans, com vários exemplos de como usar as inúmeras funcionalidades da linguagem, inclusive online, sem precisar baixar nenhuma IDE. Na parte II deste post, você irá aprender como criar o projeto Kiphy, a tela de listagem de GIFs e acessar a Giphy API para visualizar os GIFs de acordo com algum termo de pesquisa, utilizando a arquitetura Model-View-Presenter (MVP) para estruturar a camada de apresentação da app.
Até a próxima! 🙂
Saiba mais sobre Kotlin!
https://developer.android.com/kotlin/get-started.html
https://kotlinlang.org/docs/tutorials/koans.html
Deixe um comentário