5 - Um pouco sobre Kotlin
O objetivo desse capítulo não é transformar o leitor em um especialista em Kotlin, mas sim apresentar alguns conceitos importantes que serão necessários ao longo desse livro.
A linguagem Kotlin é semelhante, em alguns aspectos, à linguagem Java. Além disso, Kotlin é interoperável com Java, ou seja, em uma mesma aplicação é possível ter as duas linguagens funcionando juntas.
A documentação oficial de Kotlin é muito boa e está disponível nesse link. Além disso, também existe um curso online gratuito que pode ser acessado aqui.
Outro recurso para aprender Kotlin e praticar é esse playground oferecido pelo seu site. Se desejar, utilize esse site para testar trechos de código desse capítulo.
Os tópicos a seguir cobrem alguns pontos básicos de Kotlin, apenas demonstrando conceitos, principalmente os que se diferem de alguma forma de outras linguagens como o Java.
5.1 - O básico de Kotlin
Essa seção é dedicada aos pontos mais básicos de Kotlin, como declaração de variáveis, funções e expressões interessantes com a cláusula when.
5.1.1 - Declaração de variáveis
Em Kotlin, as variáveis possuem tipos definidos, mas podem ser declaradas essencialmente de duas formas:
- implícita: quando o tipo é inferido pelo valor atribuído durante sua declaração
var x = 7
var name = "Matilde"
- explícita: quando o tipo é definido explicitamente durante sua declaração
var x: Int = 8
var name: String = "Doralice"
Em Kotlin, as variáveis devem possuir um valor quando são declaradas ou terem um valor atribuído antes de serem utilizadas. Obviamente, existem contornos na linguagem para isso.
Nos dois casos apresentados anteriormente, as variáveis podem ter seus valores alterados, mas também é possível declará-las de tal forma que seus valores não possam ser alterados, com a instrução val:
val y = 7
val name = "Clotilde"
Dessa forma, essas duas variáveis não poderão sofrer alterações dentro do escopo que pertencem.
5.1.2 - String templates
Em Kotlin é possível montar strings contendo valores de outras variáveis de forma muito mais intuitiva, como no exemplo a seguir:
var z = 3
print("The value is ${z}")
Nesse caso, a expressão ${z} será substituída no resultado final com o valor da variável z:
The value is 3
5.1.3 - Condicionais
As expressões condicionais em Kotlin possuem semelhanças com outras linguagens, como em Java:
val x = 3
val y = 5
if (x > y) {
print("x is greater than y")
} else {
print("y is greater than x")
}
Porém, elas podem possuir condições para intervalos, como no trecho a seguir:
var x = 4
if (x in 1..10) {
print("It's in the range")
}
Os operadores de intervalos também podem ser utilizados em loops e em cláusulas when.
5.1.4 - Loops
A seguir um exemplo de uma iteração, utilizando a estrutura for para varrer uma lista de strings:
var dogs = listOf("Matilde", "Doralice", "Hannah", "Clotilde")
for (dog in dogs) {
println("New dog is: ${dog}")
}
E aqui um outro exemplo simples utilizando a estrutura while:
var x = 0
while (x < 10) {
println("Value is: ${x}")
x++
}
5.1.5 - Funções
Funções em Kotlin possuem uma sintaxe bem diferente de Java, além de algumas vantagens interessantes, como:
- valores padrões;
- argumentos com nomes.
A seguir um exemplo de uma função simples para somar dois inteiros e retornar seu resultado:
fun sum(a: Int, b: Int): Int {
val result = a + b
return result
}
Ela poderia ser chamada da seguinte forma:
fun main() {
val result = sum(3, 4)
print("Result: ${result}")
}
Na segunda linha, a função sum é chamada com seus parâmetros, mas ela também poderia ser invocada da seguinte forma:
fun main() {
val result = sum(a = 3, b = 4)
print("Result: ${result}")
}
Repare que agora os parâmetros são nomeados, fazendo com que o código fique mais legível, principalmente quando se tem argumentos de tipos iguais, como é o caso.
Uma outra forma de definir os parâmetros de uma função é criando parâmetros com valores padrões, como no trecho a seguir:
fun sum(a: Int, b: Int = 1): Int {
val result = a + b
return result
}
Dessa forma, se o segundo parâmetro for omitido em sua invocação, ela assumirá o valor 1, nesse caso:
fun main() {
val result = sum(a = 3)
print("Result: ${result}")
}
5.1.6 - Expressão When
A expressão when em Kotlin substitui o switch, presente em outras linguagens, porém, com muitas vantagens, pois ela pode ter condições variadas de testes. Além disso, seu resultado pode ser utilizado em retorno de funções e atribuições de variáveis.
fun testNumber(number: Int) {
when (number) {
in 1..10 -> print("Between 1 and 10")
11 -> print("Exactly 11")
else -> print("Unknown number")
}
}
As várias formas de fazer condicionais em Kotlin podem ser utilizadas dentro da expressão when.
Como dito, o resultado da expressão when pode ser utilizado para retorno e funções ou atribuições de variáveis:
fun main() {
print(testNumber(15))
}
fun testNumber(number: Int): String {
return when (number) {
in 1..10 -> "Between 1 and 10"
11 -> "Exactly 11"
else -> "Unknown number"
}
}
Na verdade também é possível chamar outras funções de dentro do when, como no trecho a seguir:
fun main() {
testNumber(7)
}
fun testNumber(number: Int) {
when (number) {
in 1..10 -> numberCondition1()
11 -> numberCondition2()
else -> numberCondition3()
}
}
fun numberCondition1() {
print("Between 1 and 10")
}
fun numberCondition2() {
print("Exactly 11")
}
fun numberCondition3() {
print("Unknown number")
}
5.1.7 - Valores nulos e checks
Em Kotlin há uma abordagem diferente para condições quando uma variável pode ter um valor nulo ou não. Por exemplo, o seguinte trecho de código não compila:
fun main() {
var name: String = "Matilde"
name = null //compilation error
print("Name: ${name}")
}
Isso quer dizer que variáveis declaradas dessa forma não podem receber valores nulos. Porém, se houver a necessidade de uma variável receber um valor nulo, durante a execução do programa, ela deve ser declarada com essa questão de forma explícita, da seguinte forma:
fun main() {
var name: String? = "Matilde"
name = null
print("Name: ${name}")
}
Repare que na declaração da variável há um sinal de ? indicando que ela pode receber nulo durante a execução do programa.
E para acessar métodos dessa variável, é necessário fazer um null check antes:
fun main() {
var name: String? = "Matilde"
if (name != null) {
if (name.length > 10) {
print("Long name...")
} else {
print("Short name...")
}
} else {
print("Name is null")
}
}
Uma vez que sem isso, há um erro de compilação:
fun main() {
var name: String? = "Matilde"
if (name.length > 10) {
print("Long name...")
} else {
print("Short name...")
}
}
Ainda existem outras vantagens dessa abordagem, principalmente quando é necessário atribuir valores em propriedades de objetos, que podem ser nulos.
5.2 - Classes e objetos
Classes em Kotlin são declaradas através da palavra-chave class:
class Dog {
}
E um objeto dessa classe pode ser criado e atribuído em uma variável, como no trecho à seguir:
fun main() {
val dog = Dog()
}
A declaração da classe também pode ser feita já com suas propriedades, como no trecho a seguir:
class Dog (var name: String, var age: Int, var color: String = "black") {
}
Nesse caso o construtor primário está presente na declaração da classe, como pode ser visto logo após seu nome, com as propriedade sendo definidas.
Perceba que também é possível utilizar valores padrões para as propriedades da classe durante a criação da sua instância:
fun main() {
val dog = Dog("Matilde", 15)
}
Nesse caso as propriedades name e age receberão os valores Matilde e 15, que foram passados como parâmetros na criação. Já a propriedade color receberá o valor black, uma vez que ele foi omitido nessa declaração, mas que poderia ter um outro valor se ele fosse passado:
fun main() {
val dog = Dog("Clotilde", 3, "multicolored")
}
A seguir serão mostrados alguns outros conceitos úteis e interessantes sobre classes em Kotlin.
5.2.1 - Bloco inicializador
É possível criar um código para ser executado no momento da criação de um objeto de uma classe, através do bloco init:
class Dog (var name: String, var age: Int, var color: String = "black") {
init {
println("Dog name: ${this.name}")
println("Dog color: ${this.color}")
}
}
Isso é útil uma vez que o construtor primário não pode ter nenhum código para se executado.
5.2.2 - Funções de classes
Obviamente uma classe em Kotlin também pode ter funções, como o exemplo a seguir:
class Dog (var name: String, var age: Int, var color: String = "black") {
init {
println("Dog name: ${name}")
println("Dog color: ${this.color}")
}
fun dogType(): String {
return when (color) {
"black" -> "The real black dog"
"blond" -> "A special blond dog"
else -> "Maybe it's colored"
}
}
}
Perceba que a declaração de uma função de uma classe segue os mesmos padrões de funções comuns.
Para invocar essa função, basta fazer como no trecho a seguir:
fun main() {
val dog = Dog("Clotilde", 15, "multicolored")
println ("Dog type: ${dog.dogType()}")
}
Como a função retorna uma string, ela já pode ser utilizada diretamente na impressão de uma mensagem de texto.
5.2.3 - Herança
Para declarar uma nova classe que herda de Dog, primeiramente é necessário deixar claro que ela pode ser herdada, através do modificador open, como mostra o trecho a seguir:
open class Dog (var name: String, var age: Int, var color: String = "black") {
}
Dessa forma, é possível criar uma outra classe (BlackDog) que herda dela:
class BlackDog (name: String, age: Int): Dog(name, age, "black") {
}
Perceba que em sua declaração, os parâmetros são passados para a classe pai (Dog).
5.2.4 - Data classes
Para classes onde a única razão é armazenar dados, é possível utilizar data classes em Kotlin, como no trecho a seguir:
data class Cat(val name: String, val color: String)
Sua criação e utilização são semelhantes a classes comuns, como pode ser visto no trecho a seguir:
fun main() {
val cat = Cat("Eek", "purple")
print("Cat name: ${cat.name} - color: ${cat.color}")
}
5.3 - Conceitos avançados
Ainda existem outros conceitos de Kotlin que serão utilizados ao longo desse livro, principalmente porque muitas bibliotecas do Android fazem uso desses conceitos. Porém, eles serão introduzidos à medida em que forem necessários. Dessa forma o leitor consegue ter uma apresentação mais prática e eficiente. A seguir, alguns desses conceitos:
- inline function;
- lambda expression;
- lateinit.
Quando alguns desses recursos de Kotlin forem necessários pela primeira vez, haverá uma breve explicação no livro para que o leitor possa ter um entendimento mais amplo.
5.4 - Conclusão
Esse capítulo introduziu alguns conceitos básicos de Kotlin, necessários para que, a partir do próximo capítulo, o leitor possa começar a construir o primeiro aplicativo para Android utilizando essa linguagem.