Comece Por Aqui: Conceitos Básicos Kotlin Parte I
Ana Laura
Posted on October 29, 2023
💟 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?
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!")
}
- 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()
eprint()
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
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"
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"
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"
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:
List
Set
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")
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
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)
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")
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çã")
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)
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"]
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
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]
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
if
ewhen
, 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`
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
👽 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
}
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
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
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'
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"
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
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`.
While
🔸 Como em outras linguagens podemos utilizar o while
de duas maneiras:
- Para executar um bloco de código enquanto uma expressão condicional é verdadeira.
while
- 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
💠 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
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:
- Inicialmente, ambas as variáveis cakesEaten e cakesBaked estão definidas como 0.
- 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.
- 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.
- 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.
- 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!
}
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
}
- 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. - A segunda função é chamada
main
. Ela é a função de entrada do programa e é onde a execução começa. Dentro da funçãomain
, nós chamamos a funçãosum(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
}
Valores de parâmetro padrão
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
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
}
👀 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
}
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
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 sejaUnit
.
🧠 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:
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.
- Depois de escolher, clique em
Open
e isso o levará para uma página que inclui uma série de desafios, veja só:
- À 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
.
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 :(.
Posted on October 29, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.