Comece Por Aqui: Conceitos Básicos Kotlin Parte I

analaura

Ana Laura

Posted on October 29, 2023

Comece Por Aqui: Conceitos Básicos Kotlin Parte I

💟 Eai, como cês tão?

Essa é a primeira parte de um conteúdo que será dividido em duas partes, onde exploraremos a documentação do Kotlin.

Antes de criar este conteúdo, pesquisei como poderia contribuir com a tradução da documentação oficial, mas em resumo, eles incentivam traduções, porém a hospedagem e publicação da tradução são de responsabilidade do tradutor, e ela não será incorporada ao site oficial da linguagem Kotlin. Mais informações, aqui

Ressalto a importância de também consultar a documentação oficial, uma vez que essa fonte é regularmente atualizada e abriga uma vasta quantidade de informações detalhadas. Além disso, a documentação do Kotlin é maravilhosa.

A proposta dessa primeira parte é falar sobre os fundamentos do Kotlin, abordando a parte inicial e básica.

Bônus: Ao final, compartilharei alguns recursos da IDE IntelliJ que podem ajudar ainda mais na sua aprendizagem sobre a linguagem. 🤯

Ana, sobre o que vamos falar? Nesta primeira parte, vou abordar:

Por que Kotlin?

Image description

Kotlin é uma linguagem de programação de código aberto desenvolvida pela JetBrains.

  • É projetada para ser compatível com o Java e é popular para o desenvolvimento de aplicativos Android.
  • É concisa, segura, legível e expressiva.
  • Resolve limitações do Java e é executada na JVM.
  • Destaca-se por sua segurança em relação a referências nulas.
  • Permite a interoperabilidade com o Java e é compatível com bibliotecas existentes.
  • Ampla adoção na comunidade de desenvolvimento, especialmente para aplicativos Android.

Meu primeiro "Hello, World" em Kotlin

💀 Olha só, para evitar possíveis "maldições" - já que há uma lenda que afirma que começar o aprendizado sem um simples "Hello, World!" pode dificultar a compreensão dos princípios básicos - vamos iniciar com um "Hello World" em Kotlin. Não que eu acredite, mas vai que né

E para isso, podemos utilizar o "playground" do Kotlin, que permite que você crie e execute códigos por lá. Legal, né?

fun main() {
    println("Hello, World!")
}
Enter fullscreen mode Exit fullscreen mode
  • Em Kotlin a palavra-chave fun é usada para declarar uma função.
  • A função main() é onde o seu programa começa.
  • O corpo de uma função é escrito entre chaves {}.
  • As funções println() e print() imprimem seus argumentos na saída padrão.

Me lembrou um pouco Python ou eu to doida?! 😅

Variáveis

🔹 Todo programa precisa ser capaz de armazenar dados, e as variáveis nos ajudam a fazer isso. Em Kotlin, você pode declarar:

  • Variáveis que não podem ser mudadas, utilizando val.
  • Variáveis mutáveis, ou seja, que podem ser alteradas, utilizando var.
  • Para atribuir um valor, usamos o operador de atribuição =.
val age = 25 //imutável

var age = 25//mutável
Enter fullscreen mode Exit fullscreen mode

As variáveis podem ser declaradas no início do seu programa, fora da função main(), por exemplo. Quando você as declara dessa forma, elas são consideradas variáveis globais.

E aqui segue a recomendação de sempre declararmos variáveis utilizando val. Utilizar var somente se necessário.

Template string

🔹 Sabe aqueles momentos em que precisamos adicionar uma ou mais variáveis em uma string? Então, é ai que as templates de strings podem ser bem úteis.

Vale lembrar que em Kotlin, um valor de string é uma sequência de caracteres entre aspas duplas ". Por exemplo:

val name = "Ana Laura Reis"
Enter fullscreen mode Exit fullscreen mode

As templates strings sempre começam com um $. Exemplo:

val age = 25

val name = "Ana Laura Reis"

val template = "My name is $name and I am $age years old"
Enter fullscreen mode Exit fullscreen mode

Nesse exemplo, $name será substituído pelo valor da variável name e $age pelo valor da variável age.

Se quiser saber mais sobre template string, dê uma olhada aqui.

Tipos básicos

🔹 Os tipos são importantes para entendermos o que estamos armazenando em uma variável ou até mesmo quais funções e propriedades aquele dado possui.

Nos exemplos acima já foi possível vermos dois tipos, int e string. A variável name é do tipo string e age do tipo int.

O Kotlin possui a habilidade de deduzir o tipo de dados de uma variável e isso é chamado de inferência de tipo.

Quando você atribui um valor inteiro à variável age, o Kotlin infere que age possui um tipo de dado numérico, especificamente do tipo Int.

Tá Ana, e o que isso significa na prática? Isso significa que o compilador "sabe" que você pode realizar operações aritméticas, como adição, subtração, multiplicação ou divisão, com a variável age.

Essa inferência de tipo é uma das características poderosas do Kotlin, pois permite que você escreva código de forma concisa e legível, sem precisar especificar explicitamente o tipo de dados a todo momento.

No geral, Kotlin possui os seguintes tipos básicos:

Tipo Descrição Exemplo
Int Números inteiros 42
Long Números inteiros longos 1234567890L
Float Números de ponto flutuante de precisão simples 3.14f
Double Números de ponto flutuante de dupla precisão 3.14
Char Caractere 'a'
String Sequência de caracteres "Olá, mundo!"
Boolean Valor lógico (verdadeiro ou falso) true ou false

Para saber mais sobre tipos básicos consulte a documentação.

E se eu quiser iniciar uma variável sem necessariamente atribuir um valor? 🤔

// variável declarada sem inicialização/sem valor atribuído
val age : Int

// variável inicializada/atribuindo o valor 25 à variável age
val age = 25

// declarando tipo e atribuindo valor
val name : String = "Ana"
Enter fullscreen mode Exit fullscreen mode

Acho que o exemplo conseguiu explicar bem!

🧠 Agora faça os exercícios para praticar o que aprendemos até aqui

Coleções

🔹 Lembra das listas, arrays? Os famosos tipos de dados que utilizamos para agrupar uma quantidade n de dados.

Em Kotlin temos algumas estruturas de dados para agrupar itens:

Coleção Descrição
List Coleção ordenada de elementos, pode conter duplicatas. Permite acesso por índices.
Set Coleção não ordenada de elementos únicos.
Map Coleção de pares chave-valor, onde cada chave é única.
ArrayList Implementação modificável de uma lista usando um array.
LinkedList Implementação modificável de uma lista usando uma estrutura de lista encadeada.
HashSet Implementação de um conjunto usando uma tabela de dispersão para armazenar elementos únicos.
TreeSet Implementação de um conjunto usando uma estrutura de árvore balanceada para armazenar elementos únicos, mantendo a ordem.
HashMap Implementação de um mapa usando uma tabela de dispersão para armazenar pares chave-valor, com chaves únicas.
TreeMap Implementação de um mapa usando uma estrutura de árvore balanceada para armazenar pares chave-valor, mantendo a ordem.

Falaremos mais detalhadamente sobre:

  1. List
  2. Set
  3. Map

Para saber mais sobre os outros tipo, consulte a documentação oficial (ou comenta aqui, quem sabe crio um conteúdo sobre isso)

1. List

Podemos ter:

  • Uma coleção ordenada de elementos.
  • Permite elementos duplicados.
  • Os elementos podem ser acessados por índices.

🔹 Para criar uma lista que não pode ser modificada (List), você utiliza a função listOf() e se quiser criar uma lista que pode ser modificada (MutableList), você utiliza a função mutableListOf().

Caso queira dizer o tipo dos elementos da sua lista, você pode fazer isso usando <>, vou mostrar alguns exemplos abaixo:

// Lista imutável
val listImmutable = listOf("Ana", "Gabriel", "Maria")

//Lista mutável e tipada

val listMutable:  MutableList<String> = mutableListOf("Ana", "Gabriel", "Maria")
Enter fullscreen mode Exit fullscreen mode

Podemos acessar qualquer elemento da lista através de seu índice:

val favoriteMusics = lisOf("Rap", "Funk", "Pagode")

favoriteMusics[0] // acessando o item "Rap" da minha lista
Enter fullscreen mode Exit fullscreen mode

Podemos acessar o primeiro e o último elemento de uma lista utilizando as funções:

  • .first()
  • .last()

Os dois exemplos acima são funções de extensão. Em resumo, os métodos são ações diretamente ligadas a uma classe, enquanto as funções de extensão são novas ações que você pode adicionar a uma classe existente sem modificar a classe original. Eles permitem estender a funcionalidade da classe de uma forma mais flexível.

Para entender melhor sobre essas funções dê uma olhada aqui.

Outra coisa legal que conseguimos pra fazer é verificar se existe um item em uma lista utilizando in.

println("Rap" in favoriteMusics)
Enter fullscreen mode Exit fullscreen mode

Também podemos adicionar e remover itens de uma lista, utilizando o .add() e .remove()

// Adiciona "Samba" no final da lista

favoriteMusics.add("Samba")

// Remove "Rap" da lista

favoriteMusics.remove("Rap")
Enter fullscreen mode Exit fullscreen mode

2. Set

  • Uma coleção que não mantém uma ordem específica.
  • Como os conjuntos não são ordenados, você não consegue acessar um item através do seu índice.
  • Contém apenas elementos únicos, não permitindo duplicatas.

Assim como nas listas também podemos criar um set mutável ou imutável:

// Imutável 

val fruits = setOf("banana", "morango", "pêra")

//Mutável

val newFruits: MutableSet<String> = mutableSetOf("banana", "morango", "pêra", "maçã")
Enter fullscreen mode Exit fullscreen mode

Ah algo interessante é que o set contêm apenas elementos únicos, todo item duplicado é descartado. 👀

Também conseguimos usar as funções add e remove para adicionarmos e removermos algum item do nosso set, caso ele seja mutável.

3. Map

  • Uma coleção de pares chave-valor, onde cada chave é única.
  • As chaves são usadas para acessar os valores associados.
  • Não mantém uma ordem específica para os pares chave-valor.

🔹 Podemos imaginar um map como um menu de comida. Você pode encontrar o preço (valor), encontrando a comida (chave) que deseja comer. Os mapas são úteis se você quiser procurar um valor sem usar um índice numerado, como em uma lista.

// Imutável 
val menu = mapOf("coxinha" to 2, "coquinha" to 3, "batata" to 10)

// Isso aqui nos retorna: {coxinha=2, coquinha=3, batata=10}

//Mutável 

val newMenu :  MutableMap<String, Int> = mapOf("esfirra" to 2, "coquinha" to 3, "brigadeiro" to 10)
Enter fullscreen mode Exit fullscreen mode

Para acessar um valor em um Map, podemos utilizar colchetes [] com sua chave, vamos supor que eu queria acessar o valor brigadeiro do map acima.

newMenu["brigadeiro"]
Enter fullscreen mode Exit fullscreen mode

Aqui também conseguimos usar as funções add e remove.😉

Se quisermos verificar se uma chave específica já existe em um map, utilizamos a função .containsKey(). Exemplo:

novoMenu.containsKey("Quibe")
//false
Enter fullscreen mode Exit fullscreen mode

Se quisermos obter as chaves ou valores de um Map, usamos as propriedades keys e values:

val menu = mapOf("coxinha" to 2, "coquinha" to 3, "batata" to 10)

menu.keys // [coxinha, coquinha, batata]
menu.values // [2, 3, 10]
Enter fullscreen mode Exit fullscreen mode

keys e values são exemplos de propriedades de um objeto. Vamos falar mais disso na parte 2 desse conteúdo que será sobre Classes 😊

🧠 Agora faça os exercícios para praticar o que aprendemos até aqui

Expressões condicionais

🔸 No Kotlin, podemos tomar decisões utilizando as estruturas if ou when.

Esse tal de when foi uma novidade 😅.

Na documentação é fortemente recomendado utilizar o when, veja só:

Se você tiver que escolher entre ife when, recomendamos usar when, pois isso leva a programas mais robustos e seguros.

Usando o if:

Para usar o if, coloque a expressão condicional entre parênteses () e a ação a ser executada se o resultado for verdadeiro entre chaves {}:

val number: Int
val check = true

if (check) {
    number = 1
} else {
    number = 2
}

println(number) // O retorno será `1`
Enter fullscreen mode Exit fullscreen mode

Usando when:

  • Use when quando você tiver uma expressão condicional com várias ramificações. When pode ser usado tanto como uma declaração quanto como uma expressão.
  • Aqui está um exemplo de como usar when como uma declaração:
  • Coloque a expressão condicional entre parênteses () e as ações a serem executadas dentro de chaves {}.
  • Use " -> " em cada ramificação para separar cada condição de cada ação.
val text = "Hello"

when (text) {
    // Verifica se o text é igual "1"
    "1" -> println("One")
    // Verifica se o text é igual a "Hello"
    "Hello" -> println("Greeting")
    // Declaração padrão
    else -> println("Unknown")     
}
// Saída: Greeting
Enter fullscreen mode Exit fullscreen mode

👽 A estrutura when em Kotlin é semelhante ao switch-case em JavaScript, por exemplo. :)

Usando o when como expressão, atribuímos isso a uma variável. Veja só:

val number: Int
val check = true

number = when {
    check -> 1
    else -> 2
}
Enter fullscreen mode Exit fullscreen mode

Legal demais, né?

Se usamos o when como uma expressão, o else é obrigatório, a menos que o compilador possa detectar que todos os casos possíveis estão cobertos pelas condições implementadas.

O when também é útil quando você precisa verificar uma cadeia de expressões booleanas, vamos dar uma olhada:

val temp = 18

val description = when {
    // Se temp < 0 for true, description recebe "very cold"
    temp < 0 -> "very cold"
    // Se temp < 10 for true, description recebe "a bit cold"
    temp < 10 -> "a bit cold"
    // Se temp < 20 é true, description recebe "warm"
    temp < 20 -> "warm"
    // description recebe "hot" caso não caia em nenhuma condição acima
    else -> "hot"             
}
println(description)
// warm
Enter fullscreen mode Exit fullscreen mode

Ranges

🔸 Em Kotlin, os ranges são uma maneira de definir intervalos de valores. Eles são usados principalmente com tipos que podem ser comparados, como números e caracteres.

Um range é definido usando o operador ".." (que é chamado de operador de range) e pode ser usado para criar intervalos fechados ou abertos. Vamos dar uma olhada em alguns exemplos:

val closedRange = 1..5
for (i in closedRange) {
    println(i)
}
// Isso imprimirá 1, 2, 3, 4, 5
Enter fullscreen mode Exit fullscreen mode

Uma coisa daora sobre ranges é que você pode criar ranges de caracteres ou substrings de strings. Veja só:

val charRange = 'a'..'z'
for (char in charRange) {
    println(char)
}
// Isso imprimirá todas as letras minúsculas de 'a' a 'z'
Enter fullscreen mode Exit fullscreen mode

Agora criando um range de substrings a partir de uma string:

val str = "Hello, Kotlin"
val substringRange = str.substring(0..4)
println(substringRange) // Isso imprimirá "Hello"
Enter fullscreen mode Exit fullscreen mode

Loops

🔸 No Kotlin, temos estruturas como o while, que permite continuar uma ação até que uma determinada condição seja satisfeita, e o for, que é utilizado para iterar um número específico de vezes, executando ações.

Vamos explorar um pouco mais essas duas estruturas.

Imagine que eu queria criar um looping que itera 5 vezes. Para isso poderia fazer um laço for:

For

for (number in 1..5) { 
    print(number)
}
// irá imprimir: 12345
Enter fullscreen mode Exit fullscreen mode

Eu posso querer percorrer uma lista que contém sabores de bolos:

val cakes = listOf("chocolate", "baunilha", "milho")

for (cake in cakes) {
    println(cake)
}
// Isso irá imprimir cada um dos sabores da lista `cakes`.
Enter fullscreen mode Exit fullscreen mode

While

🔸 Como em outras linguagens podemos utilizar o while de duas maneiras:

  1. Para executar um bloco de código enquanto uma expressão condicional é verdadeira. while
  2. Para executar o bloco de código primeiro e depois verificar a expressão condicional. do-while

💠 No primeiro caso: while:

var cakesEaten = 0
while (cakesEaten < 3) {
    println("Comeu um bolo")
    cakesEaten++
}

// Comer um bolo
// Comer um bolo
// Comer um bolo
Enter fullscreen mode Exit fullscreen mode

💠 No segundo caso: do-while:

var cakesEaten = 0
var cakesBaked = 0
while (cakesEaten < 3) {
    println("Comeu um bolo")
    cakesEaten++
}
do {
    println("assou um bolo")
    cakesBaked++
} while (cakesBaked < cakesEaten)
// Comer um bolo
// Comer um bolo
// Comer um bolo
// asse um bolo
// asse um bolo
// asse um bolo
Enter fullscreen mode Exit fullscreen mode

No código acima, temos duas variáveis, cakesEaten e cakesBaked, que estão sendo usadas para controlar o número de bolos consumidos e assados. Bora ver o que está acontecendo passo a passo:

  1. Inicialmente, ambas as variáveis cakesEaten e cakesBaked estão definidas como 0.
  2. Em seguida, há um loop while que continua até que o valor de cakesEaten seja menor que 3. Dentro desse loop, a mensagem "Eat a cake" é impressa na tela usando println, e então o valor de cakesEaten é incrementado em 1.
  3. O loop while continuará a ser executado até que cakesEaten seja igual ou maior que 3. Isso significa que o loop será executado três vezes, resultando na impressão de "Comer um bolo" três vezes e no incremento de cakesEaten para 3.
  4. Após a saída do primeiro loop, há um segundo loop do-while. Este loop imprime a mensagem "Asse um bolo" na tela usando println, e então o valor de cakesBaked é incrementado em 1.
  5. O loop do-while continuará a ser executado até que o valor de cakesBaked seja menor do que o valor de cakesEaten. Isso significa que o loop será executado três vezes, uma vez que cakesBaked é inicializado como 0 e cakesEaten é igual a 3 após a conclusão do primeiro loop. Portanto, o loop imprime "Asse um bolo" três vezes e incrementa cakesBaked para 3.

🧠 Agora faça os exercícios para praticar o que aprendemos até aqui

Funções

Usamos a palavra reservada fun para declarar uma função. Divertido, né?🤪

fun hello() {
    return println("Hello, world!")
}

fun main() {
    hello()
    // Hello, world!
}
Enter fullscreen mode Exit fullscreen mode

Fizemos algo parecido igual no início desse conteúdo, lembra?! 😅

Em Kotlin:

  • os parâmetros da função são escritos entre parênteses ().
  • cada parâmetro deve ter um tipo e vários parâmetros devem ser separados por vírgulas ,.
  • o tipo de retorno é escrito após os parênteses da função (), separados por dois pontos :.
  • o corpo de uma função é escrito entre chaves {}.(Igual no JavaScript rs)
  • O return é usado para sair ou retornar algo de uma função.

Veja um exemplo:

fun sum(x: Int, y: Int): Int {
    return x + y
}

fun main() {
    println(sum(1, 2))
    // 3
}
Enter fullscreen mode Exit fullscreen mode
  1. A primeira função é chamada sum e recebe dois parâmetros inteiros (x e y) e retorna um valor inteiro. O que essa função faz é simples: ela pega os valores de x e y, soma, em seguida, retorna o resultado da soma.
  2. A segunda função é chamada main. Ela é a função de entrada do programa e é onde a execução começa. Dentro da função main, nós chamamos a função sum(1, 2), que calcula a soma de 1 e 2 (resultando em 3) e, em seguida, usamos a função "println" para imprimir o resultado.

Nomeando argumentos

Incluir nomes de parâmetros torna seu código mais fácil de ler. Se você incluir nomes de parâmetros, poderá escrevê-los em qualquer ordem.

fun printMessageWithPrefix(message: String, prefix: String) {
    println("[$prefix] $message")
}

fun main() {
    // Uses named arguments with swapped parameter order
    printMessageWithPrefix(prefix = "Log", message = "Hello")
    // Isso imprime: [Log] Hello
}
Enter fullscreen mode Exit fullscreen mode

Valores de parâmetro padrão

fun printMessageWithPrefix(message: String, prefix: String = "Info") {
    println("[$prefix] $message")
}
Enter fullscreen mode Exit fullscreen mode

Caso nenhum valor seja passado para o parâmetro prefix, o valor padrão será "Info".

fun main() {

    printMessageWithPrefix("Hello", "Log") 
    // [Log] Hello


    printMessageWithPrefix("Hello")        
    // [Info] Hello

    printMessageWithPrefix(prefix = "Log", message = "Hello")
    // [Log] Hello
}
Enter fullscreen mode Exit fullscreen mode

👀 Você pode pular parâmetros específicos com valores padrão, em vez de omiti-los. No entanto, após pular o primeiro parâmetro, você deve nomear todos os parâmetros subsequentes.

Função sem retorno

💠 Se sua função não retorna um valor útil, o tipo de retorno dela é Unit. Unit é um tipo com apenas um valor - Unit.

Você não precisa declarar explicitamente que a função retorna Unit em seu corpo de função. Isso significa que você não precisa usar a palavra-chave return ou declarar um tipo de retorno, vamos ver alguns exemplos:

fun printMessage(message: String) {
    println(message)
    // `return Unit` ou `return` é opcional
}

fun main() {
    printMessage("Hello")
    // Hello
}
Enter fullscreen mode Exit fullscreen mode

Funções single-expression

🔸 Para tornar nosso código mais conciso, podemos usar funções de "única expressão". Por exemplo, podemos encurtar a nossa função sum() desse jeito aqui:

// antes:

fun sum(x: Int, y: Int): Int {
    return x + y
}

// depois:

fun sum(x: Int, y: Int) = x + y
Enter fullscreen mode Exit fullscreen mode

Você pode remover as chaves {} e declarar o corpo da função usando o operador de atribuição =. E devido à inferência de tipos do Kotlin, você também pode omitir o tipo de retorno. A função sum() se torna então uma única linha 😱

👀 Omissão do tipo de retorno é apenas possível quando sua função não possui corpo ({}), a menos que o tipo de retorno de sua função seja Unit.

🧠 Agora faça os exercícios para praticar o que aprendemos até aqui


💎 Bônus

Nessa parte bônus, irei explicar como fazer o download do IntelliJ IDEA gratuitamente e como aproveitar ao máximo essa ferramenta para aprimorar seus conhecimentos em Kotlin, através de prática direta na própria IDE 😻

👁️ Antes de instalar o IntelliJ IDEA, tenha certeza que o JDK está instalado na sua máquina. Você pode baixar o JDK mais recente diretamente do site da Oracle ou optar por uma distribuição OpenJDK, que é uma alternativa de código aberto. Certifique-se de fazer todas as configurações necessárias.

  • Baixe o IntelliJ aqui nesse link. Informação importante: A primeira opção que aparece é a Ultimate, não é essa que queremos de um scroll para baixo e faça o download da Community Edition.
  • Depois de baixar, siga todos os procedimentos de acordo com seu sistema operacional.

Quando você abrir o IntelliJ IDEA, provavelmente você terá algo parecido com isso daqui:

Print da IDE IntelliJ mostrando a página inicial da IDE

Se quisermos criar um novo projeto Kotlin, podemos clicar em new project e aqui não tem muito segredo, você deve escolher um nome para seu projeto e colocar a linguagem desejada, que no nosso caso é o Kotlin e logo após clicar em create.

Usando a IDE para aprender/praticar Kotlin 🤯

  • Na tela inicial, à esquerda, clique em "Learn".
  • Em seguida, localize e clique em "New Course" no lado direito.
  • Após clicar em "New Course", uma nova página será aberta. No lado esquerdo, clique em "Marketplace". Neste caso, estou fazendo o seguinte curso.

print da IDE IntelliJ mostrando a parte de cursos e desafios, circulando o curso kotlin Koans, demonstrando qual curso eu selecionei

  • Depois de escolher, clique em Open e isso o levará para uma página que inclui uma série de desafios, veja só:

print da tela de courses da IDE IntelliJ

  • À esquerda, você encontrará uma lista de todos os desafios propostos.
  • No centro é onde você irá resolver esses desafios, ou seja, onde você irá codar.
  • À direita, você encontrará a descrição do desafio, ou seja, as instruções sobre o que precisa ser feito.

Depois que finalizar o desafio para verificar se está correto e prosseguir para o próximo, basta clicar em check.

print da tela de courses da IDE IntelliJ


Chegamos ao fim desta primeira parte, espero que tenham gostado!

Até a próxima e bons estudos!

Se você leu até o final reage com um 💟 se não, não precisa reagir :(.


Imagem com a logo da plataforma Buy Me a Coffee

💖 💪 🙅 🚩
analaura
Ana Laura

Posted on October 29, 2023

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

Sign up to receive the latest update from our blog.

Related