Skip to main content

Kotlin



Introdução ao Kotlin


O Kotlin é uma linguagem multiplataforma, orientada a objetos, criada pela JetBrains em 2011. É uma linguagem de programação moderna, estática e fortemente tipada. Ela foi projetada para ser concisa, expressiva, segura e interoperável com o Java, o que a torna uma escolha popular para o desenvolvimento de aplicativos Android, servidores web, aplicativos desktop, aplicações backend e muito mais.


Uma das principais vantagens do Kotlin é sua interoperabilidade perfeita com o Java. Isso significa que os desenvolvedores podem facilmente integrar o Kotlin em projetos existentes em Java e vice-versa, aproveitando as bibliotecas e ferramentas disponíveis em ambas as plataformas.


Além disso, o Kotlin oferece recursos poderosos, como inferência de tipos, extensões de funções, funções de ordem superior, suporte a programação funcional, o que ajuda a reduzir erros comuns de programação e a tornar o código mais robusto e legível.


o que é uma linguagem fortemente tipada?

Uma linguagem de programação é considerada fortemente tipada quando ela impõe restrições rígidas sobre como os tipos de dados podem ser usados em um programa. Em outras palavras, em uma linguagem fortemente tipada, os tipos de dados devem ser explicitamente definidos e os tipos incompatíveis não são automaticamente convertidos.


Isso significa que, em uma linguagem fortemente tipada, você precisa especificar o tipo de cada variável quando a declara, e a linguagem não permite operações que misturem tipos de dados de maneira incompatível. Por exemplo, em uma linguagem fortemente tipada, você não pode adicionar um número a uma string sem fazer uma conversão explícita de tipo.




Instalando o Kotlin


Instale a versão a IDE IntelliJ IDEA CE (Community Edition). Ela pode ser baixada no site oficial, basta descer um pouco a página para ver a versão gratuita. Após fazer o download, vamos instalar:

Terminal
# Extraia o conteúdo do arquivo compactado e Entre no diretório do kotlin:
tar xf ideaIC-2023.3.6.tar.gz && cd idea-IC-233.15026.9/

# Agora vamos executar a IDE:
bin/idea.sh

# Para facilitar, crie um alias no bash:
echo "alias kotlin='~/Downloads/kotlin_installer/idea-IC-233.15026.9/bin/idea.sh'" >> ~/.bash_aliases

# Faça uma re-leitura do bash_aliases:
source ~/.bash_aliases

Agora vamos instalar o Java Development Kit (JDK), acesse a página oracle.com para baixar o JDK em formato .deb, é mais simples de ser instalado. Uma alternativa é instalar o pacote openjdk-11-jdk.

Terminal
# Após baixar o pacote, vamos instalar ele:
sudo dpkg -i ~/Downloads/jdk-22_linux-x64_bin.deb

JDK vs JRE e JSE

O JRE (Java Runtime Environment) é necessário para executar aplicativos Java, incluindo aplicativos Kotlin compilados que são executados na JVM (Java Virtual Machine). No entanto, para desenvolver em Kotlin, você precisará do JDK (Java Development Kit), que inclui o JRE, além de ferramentas de desenvolvimento como compilador e bibliotecas de classes.


Se você baixar apenas o JRE do site oficial da Oracle, você terá o ambiente necessário para executar aplicativos Java, mas não para desenvolver em Kotlin. Você precisará do JDK para compilar e desenvolver código Kotlin.


O Java SE (Java Standard Edition) é uma edição do Java que fornece o conjunto básico de APIs e funcionalidades para desenvolvimento de aplicativos Java de propósito geral. O Java SE inclui o JDK (Java Development Kit), que é necessário para desenvolver e compilar aplicativos em Java, incluindo aqueles escritos em Kotlin que são executados na JVM (Java Virtual Machine).



Iniciando o primeiro projeto no IntelliJ


Primeiro execute o IntelliJ, pode usar o alias no terminal chamado kotlin. Depois clique na opção o ícone de + (New Project). Depois é só preencher os dados, caso não tenha instalado o JDK ainda, é possível instalar indo na opção JDK e selecione Download JDK..., aparecerá outra janela para escolher a versão que deseja instalar.


Em build system selecione o Maven e na Language selecione Kotlin. A imagem abaixo representa meu projeto inicial.

Iniciando o projeto no IntelliJ pela primeira vez


O que é o Maven:

O Maven é uma ferramenta que auxilia no gerenciamento de dependências, no processo de build, entre outras funcionalidades importantes dentro do projeto. Com ele podemos especificar as dependências do projeto em um arquivo de configuração chamado pom.xml (Project Object Model). Este arquivo contém informações sobre o projeto, suas dependências, plugins Maven a serem usados e outros detalhes relacionados à construção.



Variáveis


Uma variável é um elemento básico em programação que representa um local na memória do computador onde valores podem ser armazenados e manipulados durante a execução de um programa. Ela possui um nome único que serve como uma referência para acessar seu conteúdo. Resumindo, podemos usar variáveis para armazenar valores.


Existem dois tipos principais de variáveis em Kotlin: val e var.

  • val
    Usado para declarar constantes ou variáveis imutáveis. Uma vez que uma variável é inicializada usando val, seu valor não pode ser alterado posteriormente. A inicialização deve ocorrer no momento da declaração. Isso significa que não é possível deixar uma variável val sem valor no momento da declaração. Então você deve atribuir um valor a ela no momento da declaração.

    Terminal
    val PI = 3.14159
    val nome: String = "João"
  • var
    É usado para declarar variáveis mutáveis. Ao contrário de val, o valor de uma variável declarada com var pode ser alterado depois que ela é inicializada. Não é necessário inicializá-la no momento da declaração.

    Terminal
    var contador: Int
    contador = 0


lateinit


Em Kotlin, temos a palavra-chave lateinit que é usada para declarar atributos que não precisam ser inicializadas imediatamente no momento em que são declaradas. Ou seja, esses atributos podem ser inicializadas posteriormente antes de serem utilizados.


A principal diferença entre um atributo declarado com lateinit e um atributo declarado como nullable (var prop: Type?) é que um atributo lateinit deve ser inicializado antes de ser acessado, caso contrário, uma exceção UninitializedPropertyAccessException ocorrerá.


Aqui está um exemplo de como você pode usar lateinit:

class Exemplo {
lateinit var nome: String // Declaração de uma propriedade 'nome' usando lateinit

fun inicializarNome() {
nome = "João" // Inicializando a propriedade 'nome'
}

fun imprimirNome() {
println(nome) // Imprimindo a propriedade 'nome'
}
}

fun main() {
val exemplo = Exemplo()
exemplo.inicializarNome() // Inicializando a propriedade 'nome'
exemplo.imprimirNome() // Imprimindo a propriedade 'nome'
}

Quando você declara uma variável dentro de uma função, como main(), o Kotlin não exige que ela seja inicializada imediatamente. No entanto, quando você declara um atributo (dentro de uma classe), todos os atributos devem ser inicializados ou terem um valor padrão no momento da declaração, a menos que você as marque com lateinit ou as torne eles nulos adicionando ? ao tipo. Vejamos um exemplo:

fun main() {
val teste: String
}

class Var {
val teste1: String
}

O código acima não funciona e está errado, já que o compilador me força a inicializar o atributo teste1, isso se deve pela explicação acima, já na main podemos fazer sem nenhum problema e inicializar a variável teste depois.



Tipos de variáveis


Todos os tipos básicos de variáveis do Java estão presentes aqui no Kotlin. Mas, aqui em Kotlin, todos os tipos são objetos. A tabela abaixo mostra os tipos básicos de variáveis que podem ser declaradas:

TipoDescriçãoTamanho em bits
LongNúmeros inteiros variando de -9,223,372,036,854,775,808 (-2^63) até 9,223,372,036,854,775,807 (2^63 - 1)64
IntNúmeros inteiros variando de -2,147,483,648 (-2^31) até 2,147,483,647 (2^31 - 1)32
ShortNúmeros inteiros variando de -32768 até 3276716
ByteNúmeros inteiros variando de -128 até 1278
DoublePonto flutuante que exibe até 16 dígitos decimais fazendo arredondamento deles.64
FloatPonto flutuante que exibe até 7 dígitos decimais fazendo arredondamento deles. Para decalar o float seria assim: var numberfloat: Float = 2.7182818284f.32
CharCaractere Unicode. Para declarar fazemos assim: val char1: Char = 'a' (precisa usar aspas simples).16
BooleanValor verdadeiro/falso8
StringSequência de caracteres, na declaração usamos aspas duplas.2
AnyEspecifica que a variável pode armazenar valores de qualquer tipo em Kotlin64

Para converter números de um tipo para outro, veja aqui.



Null Safety


Null Safety (Segurança contra nulos) é um conceito fundamental em Kotlin projetado para ajudar os desenvolvedores a evitar erros comuns relacionados a valores nulos (null). A segurança contra nulos é incorporada diretamente na linguagem Kotlin e oferece suporte robusto para lidar com valores nulos de forma segura e explícita.



Tipos Nullable e Tipos Não-Nullable


Em Kotlin, um tipo de dado pode ser declarado como nullable adicionando ? ao seu tipo. Isso indica que a variável pode armazenar um valor do tipo declarado ou o valor nulo (null).

var nome: String? = "João" // Pode ser uma string ou null

Se um tipo não tiver um ? após ele, isso significa que ele não pode armazenar um valor nulo.

var idade: Int = 30 // Não pode ser nulo


Safe Calls


O operador ?. é usado para chamar métodos ou acessar propriedades em uma variável que pode ser nula. Se a variável for nula, a chamada de método ou acesso à propriedade será ignorada e o resultado será nulo.

val tamanho: Int? = nome?.length // Retorna o comprimento da string ou null se nome for null


Operador Elvis


O operador Elvis (?:) é usado para fornecer um valor padrão caso o valor à esquerda seja nulo.

val tamanhoString: Int = nome?.length ?: -1 // Se nome for nulo, o valor padrão -1 será atribuído a tamanhoString


Assegurar que o valor não seja nulo


O operador !!. é usado para afirmar que uma variável não é nula e acessa seu valor diretamente. É importante usar o operador !! com cuidado, pois ele pode resultar em um NullPointerException se a variável for realmente nula.

fun main() {
var nome: String? = "joao" // Variável pode ser nula
val tamanho: Int = nome!!.length // Afirmando que nome não é nulo

println("${tamanho}") // Resultado = 4
}


Funções


Uma função é uma sequência de instruções que executa uma tarefa específica. As funções podem ter zero ou mais parâmetros e podem retornar um valor ou não. Elas são declaradas com a palavra-chave fun em Kotlin. Exemplo de uma função simples em Kotlin:

fun helloWorld() {
println("Olá, mundo!")
}


Parâmetros


Os parâmetros são valores que podem ser passados para uma função para serem usados durante sua execução. Eles são definidos dentro dos parênteses após o nome da função. Em Kotlin, você precisa especificar o tipo de cada parâmetro. Exemplo de função com parâmetros em Kotlin:

fun main() {
pessoa("Joao")
}

fun pessoa(name: String) {
println("Olá, $name!")
}

Também é possível especificar a variável que estamos informando o valor.

fun main() {
pessoa(idade = 27, name = "Joao")
}

fun pessoa(name: String, idade: Int) {
println("Olá, $name!")
}

Outro detalhe é que podemos colocar um valor padrão para uma variável caso não seja informada no construtor.

fun main() {
pessoa(name = "Joao")
}

fun pessoa(name: String, idade: Int = 35) {
println("Olá, $name!, idade é $idade")
}


Return


Algumas funções em Kotlin retornam um valor após sua execução. O tipo de retorno é especificado após os parâmetros da função, seguido por um sinal de dois pontos. Se uma função não retornar nenhum valor, o tipo de retorno é Unit, que é semelhante a void em outras linguagens. Exemplo de função com retorno em Kotlin:

fun soma(a: Int, b: Int): Int {
return a + b
}

Nesse caso essa função retorna um dado do tipo inteiro, podemos ver isso pelo Int após os dois pontos : depois de declarar os parâmetros da função, veja aqui: fun soma(a: Int, b: Int): Int {.



Estruturas de condições


Estruturas de condição permitem que um programa execute diferentes blocos de código com base em uma condição lógica. Essas estruturas permitem tomar decisões e direcionar o fluxo do programa com base em diferentes cenários.


No Kotlin, você usará os operadores de comparação da seguinte forma:

  • ==: Verifica se dois valores são iguais.
  • !=: Verifica se dois valores são diferentes.
  • >: Verifica se um valor é maior que outro.
  • <: Verifica se um valor é menor que outro.
  • >=: Verifica se um valor é maior ou igual a outro.
  • <=: Verifica se um valor é menor ou igual a outro.

Esses operadores são comumente usados em estruturas de condição, como if, else if e when, para tomar decisões com base nas condições de comparação.



IF/ELSE


A estrutura if permite que um bloco de código seja executado se uma condição específica for verdadeira. O if é usado para executar um bloco de código se uma condição for verdadeira. Ele pode ser usado de duas maneiras:

Vejamos um if simples:

val numero = 10

if (numero > 5) {
println("O número é maior que 5")
}

Agora vamos adicionar um else:

val numero = 3

if (numero > 5) {
println("O número é maior que 5")
} else {
println("O número é menor ou igual a 5")
}

Agora vamos adicionar o else if, ele é usado quando você precisa avaliar várias condições em sequência.

val numero = 10

if (numero > 10) {
println("O número é maior que 10")
} else if (numero < 10) {
println("O número é menor que 10")
} else {
println("O número é igual a 10")
}


Expressão IF:


Em Kotlin, if pode ser usado como uma expressão também, o que significa que pode retornar um valor.

val numero = 10

val resultado = if (numero > 5) {
"Maior que 5"
} else {
"Menor ou igual a 5"
}

println(resultado) // Resultado: Maior que 5


IF como expressão única


Quando você tem um único comando dentro do bloco if, você pode usá-lo de forma ainda mais concisa:

val numero = 10

val mensagem = if (numero > 5) "Maior que 5" else "Menor ou igual a 5"
println(mensagem) // Resultado: Maior que 5


Return com IF


Podemos usar o return diretamente no if, economizando linhas de código.

fun main() {
println(parimpar(5)) // Resultado: impar
println(parimpar(4)) // Resultado: par
}

fun parimpar(numero: Byte): String {

return if(numero % 2 == 0) {
"par"
} else {
"impar"
}

}


When


O when em Kotlin permite substituir múltiplas instruções if-else if-else. É muito semelhante ao switch-case encontrado em outras linguagens de programação, mas com mais recursos e uma sintaxe mais expressiva.


Abaixo segue a sintaxe básica:

when (expressao) {
valor1 -> { // Bloco de código para valor1 }
valor2 -> { // Bloco de código para valor2 }
valor3, valor4 -> { // Bloco de código para valor3 ou valor4 }
in valor5..valor6 -> { // Bloco de código para valores no intervalo de valor5 a valor6 }
else -> { // Bloco de código padrão }
}

Agora vamos ver um exemplo de uso:

val x = 3

when (x) {
1 -> println("x é igual a 1")
2, 3 -> println("x é igual a 2 ou 3")
in 4..10 -> println("x está entre 4 e 10")
else -> println("x não tem uma regra definida")
}


Estruturas de repetição


As estruturas de repetição permitem a execução repetida de um bloco de código com base em determinadas condições ou contadores. Essas estruturas são valiosas para automatizar tarefas repetitivas e melhorar a eficiência dos programas.



FOR


O for em Kotlin é uma estrutura de controle de fluxo utilizada para iterar sobre elementos em uma coleção (como arrays, listas, ranges, etc.) ou para repetir um bloco de código um número específico de vezes.


Iterando sobre um range (intervalo):

for (i in 1..15) {
println("$i")
}

No for podemos usar a palavra step para especificar o incremento entre os valores do intervalo. Ele determina o quanto o valor deve aumentar ou diminuir a cada iteração do loop.

for (i in 0..10 step 2) { // Exibe de 0 até 10 em intervalos de 2
println(i)
}

Já o downTo é usado para criar um intervalo que diminui progressivamente de um valor inicial para um valor final. Ele é frequentemente usado em conjunto com step para especificar o decremento entre os valores do intervalo.

for (i in 10 downTo 0 step 2) { // Vai de 10 para 0 em intervalos de 2.
println(i)
}

O listOf é uma função em Kotlin que cria uma lista imutável (read-only) contendo os elementos passados como argumentos.

val lista = listOf("a", "b", "c")

Você pode acessar os elementos da lista pelo seu índice, mas não pode adicionar, remover ou modificar elementos na lista, pois ela é imutável.



Controle de Loop


Dentro de um loop for, você pode usar as instruções break e continue para controlar o fluxo do loop:

  • break: Interrompe o loop e sai dele.
  • continue: Pula para a próxima iteração do loop.

for (i in 1..10) {
if (i == 5) {
break // interrompe o loop quando i for igual a 5
}

if (i % 2 == 0) {
continue // pula para a próxima iteração se 'i' for par
}

println(i)
}


While


O while em Kotlin é uma estrutura de controle de fluxo que repete um bloco de código enquanto uma condição específica é verdadeira. Ele é útil quando você precisa executar um bloco de código repetidamente, mas não sabe antecipadamente quantas vezes precisará repeti-lo.


Dentro de um loop while, também podemos usar as instruções break e continue para controlar o fluxo do loop.


A sintaxe básica é:

while (condicao) {
// Bloco de código a ser executado enquanto a condição for verdadeira
}

  • condicao: Uma expressão booleana que é avaliada a cada iteração do loop. Enquanto essa condição for verdadeira, o loop continua sendo executado. Quando a condição se torna falsa, o loop é encerrado.

Segue Exemplo:

var contador = 0

while (contador < 5) {
println("Contador: $contador")
contador++
}

Neste exemplo, o while será executado repetidamente enquanto a variável contador for menor que 5.
A cada iteração, o valor de contador é impresso e incrementado em 1.
Quando contador atinge 5, a condição contador < 5 se torna falsa e o loop é encerrado.



Do-While


O do-while em Kotlin é uma estrutura de controle de fluxo semelhante ao while, mas com uma diferença fundamental: no do-while, o bloco de código é executado pelo menos uma vez, mesmo que a condição seja falsa.


A sintaxe básica é:

do {
// Bloco de código a ser executado
} while (condicao)

  • condicao: Uma expressão booleana que é avaliada após cada execução do bloco de código. Enquanto essa condição for verdadeira, o loop continua sendo executado. Quando a condição se torna falsa, o loop é encerrado.

Segue Exemplo:

var contador = 6

do {
println("Contador: $contador")
contador++
} while (contador < 5)

Neste exemplo, o bloco de código dentro do do é executado uma vez antes que a condição seja verificada. O bloco de código é repetido enquanto a variável contador for menor que 5. Como a variável contador é 6, a condição é falsa, então o código só será executado uma vez.


Assim como no while, também podemos usar as instruções break e continue para controlar o fluxo do loop do-while.



Classes


Uma classe em Kotlin é uma estrutura que pode conter propriedades (variáveis) e métodos (funções). É como um modelo ou um plano para criar objetos. Por exemplo:

Main.kt
class Pessoa {
var nome: String = ""
var idade: Int = 0

fun dizerNome() {
println("Meu nome é $nome")
}
}

Neste exemplo, Pessoa é uma classe com duas propriedades (nome e idade) e um método (dizerNome()).



Composição de Classes


Composição de classe é um princípio de design em que uma classe contém uma instância de outra classe como parte de sua estrutura. Basicamente nós referenciamos um objeto de uma classe como um atributo da outra classe.


Na composição de classe, a classe que contém os objetos é responsável por criar e gerenciar esses objetos.

Main.kt
class Address(var street: String, var number: Int) {

}

class Pessoa(val address: Address) {
var nome: String = ""
var idade: Int = 0

fun dizerNome() {
println("Meu nome é $nome, tenho $idade anos de idade e moro na ${address.street}, número ${address.number}.")
}
}

fun main() {
val address = Address("Faria lima", 123)
val pessoa1 = Pessoa(address)

pessoa1.nome = "João"
pessoa1.idade = 30

pessoa1.dizerNome() // Saída: Meu nome é João, tenho 30 anos de idade e moro na Faria lima, número 123.
}

A classe Address e a classe Pessoa são classes independentes uma da outra. O que estamos fazendo é criar uma instância da classe Address na função main() e passando essa instância como argumento para o construtor da classe Pessoa.


Outra forma de fazer isso seria:

Main.kt
class Carro(var cor: String, var ano: Int, var dono: Dono) {

}

data class Dono(var nome: String, var idade: Int) {

}

fun main() {
var carro = Carro("Cinza", 1997, Dono("joao", 29))

println("A cor do carro é ${carro.cor}, e o ano é de ${carro.ano}.")
println("O novo dono do carro se chama ${carro.dono.nome} e ele tem ${carro.dono.idade} anos de idade.")
}

uso do 'data'

No exemplo acima, temos uma classe Dono que tem duas propriedades: nome e idade. Se não for usado data class, o Kotlin não gerará automaticamente os métodos toString(), equals(), hashCode(), etc. Então, se você deseja imprimir uma instância de Dono usando println(), o Kotlin não sabe como representar esse objeto como uma string, a menos que você implemente o método toString() manualmente.


Para não precisar implementar os métodos manualmente, devemos declarar uma classe usando data class, assim o compilador automaticamente gera uma série de métodos úteis, incluindo toString(), equals(), hashCode(), entre outros, com base nas propriedades da classe e com isso consegue gerar os valores certinhos quando usamos println("${carro.dono}").



Injeção de Dependência


Injeção de dependência é um padrão de design em que as dependências de um objeto são fornecidas por outro componente externo, em vez de serem criadas internamente pelo próprio objeto. Isso permite que o objeto dependente (que precisa de outras classes para funcionar) seja desacoplado das suas dependências.


Na injeção de dependência, as dependências são "injetadas" no objeto, geralmente por um framework ou container IoC (Inversão de Controle).

@Service
class ClienteService(private val clienteRepository: ClienteRepository) {
// ...
}


Por exemplo, considerando a classe ClienteService que precisa do ClienteRepository para realizar operações de banco de dados. Em vez de ClienteService criar sua própria instância de ClienteRepository, o ClienteRepository é fornecido para ClienteService por meio de injeção de dependência.



Método


Um método é uma função que é associada a uma classe ou objeto em linguagens de programação orientadas a objetos. Os métodos definem o comportamento ou as ações que os objetos de uma classe podem executar. Eles podem acessar e manipular os dados (também conhecidos como propriedades ou campos) do objeto ao qual estão associados.


Em termos simples, um método é uma função que pertence a uma classe ou a um objeto específico. Ele é chamado através da instância da classe ou do objeto e pode ter acesso às propriedades e outros métodos da mesma classe.


Como exemplo, note o método dizerNome() no exemplo de código mostrado em Classes.



Objetos


Um objeto é uma instância de uma classe. Quando você instancia uma classe, você cria um objeto. Por exemplo:

Main.kt
class Pessoa {
var nome: String = ""
var idade: Int = 0

fun dizerNome() {
println("Meu nome é $nome")
}
}

fun main() {
val pessoa1 = Pessoa()
pessoa1.nome = "João"
pessoa1.idade = 30

pessoa1.dizerNome() // Saída: Meu nome é João
}

Neste exemplo, pessoa1 é um objeto da classe Pessoa. Você pode criar quantos objetos desejar a partir da mesma classe.



Construtores


Um construtor é um bloco de código que é chamado automaticamente quando um objeto é criado. Em Kotlin, você pode ter um ou mais construtores em uma classe. Existem dois tipos de construtores em Kotlin: primário e secundário.

  • Construtor Primário
    É declarado na própria declaração da classe. Por exemplo:

    Main.kt
    class Pessoa(var nome: String, var idade: Int) {
    // Corpo da classe
    }

    Quando você cria um objeto desta classe, você precisa passar os parâmetros do construtor primário.

    Main.kt
    val pessoa = Pessoa("Maria", 25)
  • Construtor Secundário
    É declarado dentro da classe usando a palavra-chave constructor. Por exemplo:

    Main.kt
    class Pessoa {
    var nome: String = ""
    var idade: Int = 0

    constructor(nome: String, idade: Int) {
    this.nome = nome
    this.idade = idade
    }
    }

    Você pode ter múltiplos construtores secundários na mesma classe.


O this é uma palavra-chave que se refere ao objeto atual dentro do contexto em que está sendo utilizado. Ela é comumente usada para distinguir entre variáveis locais e variáveis de instância quando elas têm o mesmo nome. O this desempenha um papel semelhante ao self em Python.



Collection


As Collections (ou coleções) em Kotlin são estruturas de dados que permitem armazenar e manipular grupos de elementos de maneira eficiente. Elas são fundamentais para muitos tipos de aplicativos, pois fornecem maneiras convenientes de trabalhar com conjuntos de dados, como listas, conjuntos, mapas e outras estruturas.


A biblioteca padrão de Kotlin fornece uma variedade de coleções prontas para uso, cada uma com suas próprias características e finalidades. Aqui estão alguns dos tipos de coleções mais comuns em Kotlin:

  1. Listas (List): Uma coleção ordenada de elementos que permite acessar os elementos por índice. Os elementos em uma lista podem ser duplicados.

  2. Conjuntos (Set): Uma coleção de elementos únicos, onde cada elemento aparece apenas uma vez. Os conjuntos não têm uma ordem definida.

  3. Mapas (Map): Uma coleção de pares chave-valor, onde cada chave é única. Os mapas permitem recuperar valores com base em suas chaves.

  4. ArrayLists (ArrayList): Implementação de lista mutável baseada em array. Ela oferece acesso aleatório eficiente aos elementos e pode crescer dinamicamente conforme necessário.

  5. HashSet, LinkedHashSet e TreeSet: Implementações de conjuntos que representam um conjunto de elementos únicos. HashSet não garante a ordem dos elementos, LinkedHashSet mantém a ordem de inserção e TreeSet mantém os elementos classificados em ordem natural ou usando um comparador personalizado.

  6. HashMap, LinkedHashMap e TreeMap: Implementações de mapas que associam chaves a valores. HashMap não garante a ordem dos pares chave-valor, LinkedHashMap mantém a ordem de inserção e TreeMap mantém os pares chave-valor classificados em ordem de chave.

  7. MutableList, MutableSet e MutableMap: Versões mutáveis das coleções mencionadas acima, que permitem adicionar, remover e modificar elementos após a criação.



List


Para declarar uma lista em Kotlin, você pode usar uma das seguintes formas:

  • Utilizando a função listOf():
    Cria uma lista que não podemos adicionar, remover ou modificar elementos na lista, pois ela é imutável.

    val lista: List<Tipo> = listOf(elemento1, elemento2, elemento3, ...)

    Substitua Tipo pelo tipo dos elementos que você deseja armazenar na lista e elemento1, elemento2, etc., pelos elementos que você deseja incluir na lista.

    val numeros: List<Int> = listOf(1, 2, 3, 4, 5)

    val nomes: List<String> = listOf("João", "Maria", "Carlos")

    val nomes1: listOf("João", "Maria", "Carlos") // Não precisa especificar o tipo
  • Utilizando a função mutableListOf():
    Você pode usar a função mutableListOf() para criar uma lista mutável, onde os elementos podem ser adicionados, removidos ou modificados após a criação.

    val numerosMutaveis: MutableList<Int> = mutableListOf(1, 2, 3, 4, 5)

    val nomesMutaveis: MutableList<String> = mutableListOf("João", "Maria", "Carlos")

A tabela abaixo mostra as operações que podemos usar em um objeto do tipo List em Kotlin:

OperaçãoDescrição
sizeRetorna o número de elementos na lista.
isEmpty()Retorna true se a lista estiver vazia, caso contrário, retorna false.
contains(elemento)Verifica se a lista contém o elemento especificado.
get(index)Retorna o elemento na posição especificada na lista.
indexOf(elemento)Retorna o índice da primeira ocorrência do elemento na lista, ou -1 se não encontrar.
lastIndexOf(elemento)Retorna o índice da última ocorrência do elemento na lista, ou -1 se não encontrar.
subList(startIndex, endIndex)Retorna uma lista contendo os elementos entre os índices especificados.
toList()Retorna uma cópia da lista.
listIterator()Retorna um iterador sobre os elementos na lista.
iterator()Retorna um iterador sobre os elementos na lista.
forEach{}Itera sobre os elementos da lista e executa uma ação para cada elemento.
forEachIndexed{}Itera sobre os elementos da lista e executa uma ação para cada elemento, passando o índice como argumento.
filter{}Retorna uma lista contendo apenas os elementos que correspondem ao predicado fornecido.
map{}Transforma cada elemento da lista usando a função fornecida e retorna uma lista dos resultados.
reduce{}Combina os elementos da lista usando uma operação e retorna o resultado.
sorted()Retorna uma lista classificada em ordem crescente.
sortedBy{}Retorna uma lista classificada com base no resultado da função fornecida.
distinct()Retorna uma lista contendo apenas elementos distintos.
plus()Retorna uma nova lista contendo todos os elementos da lista original mais os elementos especificados.
add(elemento)Adiciona o elemento especificado ao final da lista.
addAll(colecao)Adiciona todos os elementos da coleção especificada ao final da lista.
remove(elemento)Remove a primeira ocorrência do elemento especificado da lista, se presente.
removeAt(indice)Remove o elemento na posição especificada na lista.
removeAll(colecao)Remove todos os elementos da lista que estão presentes na coleção especificada.
removeIfPermite remover elementos de uma coleção que correspondam a determinados critérios definidos por um predicado.
listaMutavel[indice] = novoValorModifica o elemento na posição especificada na lista, atribuindo um novo valor a ele.


forEach


O forEach em Kotlin é uma função de ordem superior que permite iterar sobre os elementos de uma coleção e executar uma ação para cada elemento. Ele simplifica a iteração sobre os elementos de uma lista, conjunto, mapa ou qualquer outra coleção.


Sintaxe Básica:

colecao.forEach { elemento ->
// Bloco de código a ser executado para cada elemento
}

  • colecao: A coleção sobre a qual você deseja iterar.
  • elemento: Uma referência ao elemento atual na iteração.

Agora vejamos um exemplo:

val lista = listOf(1, 2, 3, 4, 5, 6)

lista.forEach {
println(it)
}

O it é uma variável implícita que pode ser usada dentro de lambdas ou funções de ordem superior para se referir ao parâmetro único passado para essa lambda ou função.


No exemplo acima, it refere-se a cada elemento da lista durante a iteração.


Índice


Se você precisar acessar o índice de cada elemento durante a iteração, pode usar forEachIndexed:

fun main() {
val lista = listOf(1, 2, 3, 4, 5, 6)

lista.forEachIndexed { index, elemento ->
println("O elemento $elemento está na posição $index")
}

}

Resultado do código acima

O elemento 1 está na posição 0 O elemento 2 está na posição 1 O elemento 3 está na posição 2 O elemento 4 está na posição 3 O elemento 5 está na posição 4 O elemento 6 está na posição 5


Isso é útil quando você precisa de informações sobre a posição de cada elemento na coleção durante a iteração.



Set


Em Kotlin, Set é uma coleção que representa um conjunto de elementos únicos, onde cada elemento aparece apenas uma vez. Diferentemente de uma lista, um conjunto não mantém uma ordem específica para seus elementos. Isso significa que a ordem em que os elementos são armazenados em um conjunto pode variar e não pode ser garantida.


Aqui estão algumas características importantes de um Set em Kotlin:

  1. Elementos Únicos: Um conjunto não pode conter elementos duplicados. Se você tentar adicionar um elemento que já está presente no conjunto, ele será ignorado.

  2. Sem Ordem Específica: Os elementos em um conjunto não têm uma ordem específica. Isso significa que não é possível acessar os elementos de um conjunto por meio de índices.

  3. Eficiência na Verificação de Existência: Um conjunto é otimizado para verificação de existência rápida de elementos. Isso torna os conjuntos ideais quando você precisa verificar se um elemento específico está presente ou não.


Você pode criar um conjunto usando a função setOf() para criar um conjunto imutável ou mutableSetOf() para criar um conjunto mutável.

fun main() {
val conjuntoImutavel = setOf("a", "b", "c") // Conjunto imutável

val conjuntoMutavel = mutableSetOf("x", "z", "y", "z", "x") // Conjunto mutável

println(conjuntoMutavel)
println(conjuntoImutavel)

}

Resultado do código acima

[x, z, y]

[a, b, c]


Para adicionar elementos a um conjunto mutável, podemos usar o método add(elemento). Para remover elementos, podemos usar o método remove(elemento).

conjuntoMutavel.add("w") // Adiciona o elemento "w"

conjuntoMutavel.remove("x") // Remove o elemento "x"

Podemos verificar se um elemento está presente em um conjunto usando o operador in.

val conjuntoImutavel = setOf("a", "b", "c")

if ("a" in conjuntoImutavel) {
println("O elemento 'a' está presente no conjunto imutável.")
}

Podemos iterar sobre os elementos de um conjunto usando um loop for ou a função forEach.

fun main() {
val conjuntoImutavel = setOf("a", "b", "c") // Conjunto imutável

val conjuntoMutavel = mutableSetOf("x", "z", "y", "z", "x") // Conjunto mutável

for (elemento in conjuntoImutavel) {
println(elemento)
}

conjuntoMutavel.forEach { elemento ->
println(elemento)
}

}

Resultado do código acima

a

b

c

x

z

y



Map


Em Kotlin, um Map é uma coleção que armazena pares de chave-valor, onde cada chave é única e mapeia para um único valor. Os mapas são úteis para associar valores a chaves e recuperar esses valores posteriormente usando as chaves correspondentes. Um Map é semelhante a um dicionário em outras linguagens de programação.


Aqui estão algumas características importantes de um Map em Kotlin:

  1. Chaves Únicas: Cada chave em um mapa é única. Isso significa que não pode haver chaves duplicadas no mapa; cada chave mapeia para um único valor.

  2. Recuperação Eficiente de Valores: Um mapa é otimizado para recuperação eficiente de valores com base em suas chaves. Isso torna os mapas ideais para realizar operações de pesquisa e recuperação de dados.

  3. Sem Ordem Específica: A ordem dos elementos em um mapa não é garantida. Isso significa que a ordem em que os pares chave-valor são armazenados pode variar e não pode ser confiável.

Aqui está como você pode criar e trabalhar com um Map em Kotlin:


>

Podemos criar um mapa usando a função mapOf() para criar um mapa imutável ou mutableMapOf() para criar um mapa mutável.

val mapaImutavel = mapOf("a" to 1, "b" to 2, "c" to 3) // Mapa imutável

val mapaMutavel = mutableMapOf("x" to 10, "y" to 20, "z" to 30) // Mapa mutável

Para acessar ou modificar elementos em um mapa, você pode usar operações de indexação.

val valorA = mapaImutavel["a"] // Armazena o valor associado à chave "a"

println(mapaImutavel["a"]) // Exibe o valor associado à chave "a"

mapaMutavel["x"] = 100 // Modifica o valor associado à chave "x"

Para adicionar um novo par chave-valor a um mapa mutável, podemos usar o método put(chave, valor). Para remover um par chave-valor, podemos usar o método remove(chave).

mapaMutavel.put("w", 40) // Adiciona o par chave-valor "w" e 40

mapaMutavel.remove("y") // Remove o par chave-valor com chave "y"

Podemos verificar se uma chave está presente em um mapa usando o operador in.

if ("a" in mapaImutavel) {
println("A chave 'a' está presente no mapa imutável.")
}

Também podemos iterar sobre os pares chave-valor de um mapa usando um loop for ou a função forEach.

for ((chave, valor) in mapaImutavel) {
println("Chave: $chave, Valor: $valor")
}

println("")

mapaMutavel.forEach { (chave, valor) ->
println("Chave: $chave, Valor: $valor")
}

chave e valor são nomes que nós podemos mudar para qualquer coisa.


Resultado do código acima

Chave: a, Valor: 1 Chave: b, Valor: 2 Chave: c, Valor: 3


Chave: x, Valor: 10 Chave: y, Valor: 20 Chave: z, Valor: 30



companion object


Em Kotlin, companion object é uma construção que permite definir membros de uma classe que podem ser acessados diretamente na classe, sem a necessidade de criar uma instância dela. É semelhante ao que em algumas outras linguagens é chamado de métodos ou propriedades estáticas.


Aqui está como você pode usar companion object:

class MinhaClasse (nome: String, idade: Int) {

companion object {
fun metodoEstatico() {
println("Este é um método estático")
}
val CONSTANTE_ESTATICA = 42
}

}

class Minha (nome: String, idade: Int) {
fun metodoEstatico() {
println("Este é um método estático")
}
}

fun main() {
var instanciaClasse = MinhaClasse.metodoEstatico() // chamando diretamente o método metodoEstatico() na classe sem criar uma instância dela.

var instanciaClasseMinha = Minha("joao", 30).metodoEstatico() // Precisa instanciar para chamar o método.
}

  • companion object: Declara um objeto de companheiro dentro da classe MinhaClasse.
  • fun metodoEstatico() { ... }: Define um método estático chamado metodoEstatico() dentro do objeto de companheiro. Esse método pode ser chamado diretamente na classe sem criar uma instância dela: MinhaClasse.metodoEstatico().
  • val CONSTANTE_ESTATICA = 42: Define uma propriedade estática chamada CONSTANTE_ESTATICA dentro do objeto de companheiro. Esta é uma constante que pode ser acessada diretamente na classe sem criar uma instância dela: MinhaClasse.CONSTANTE_ESTATICA.

Você pode usar companion object para vários propósitos, incluindo:

  • Métodos e propriedades de utilidade que pertencem à classe, mas não dependem de instâncias específicas.
  • Fornecer um ponto de entrada para a criação de instâncias da classe (por meio de funções de fábrica).
  • Definir constantes relacionadas à classe.


FlyWay


O Flyway é uma ferramenta de controle de versão do banco de dados. Com o FlyWay podemos gerenciar e versionar as alterações do esquema de banco de dados, garantindo que as atualizações do esquema sejam aplicadas de maneira controlada e repetível em diferentes ambientes.


Uma ferramenta como esta permite:

  • Sincronizar o banco de dados com a versão da aplicação;
  • Saber quais scripts SQL foram executados ou não;
  • Automatizar a execução dos scripts;
  • Criar um banco de dados do zero;
  • Permite criar um rollback de mudanças no banco de dados (útil em casos raros).


Fontes


https://www.alura.com.br/artigos/kotlin?_gl=1*j5hlef*_ga*MTIwOTcyNTE1My4xNzA4NzEwNTA2*_ga_1EPWSW3PCS*MTcxMjE4MzMxNi44LjEuMTcxMjE4Mzc3Mi4wLjAuMA..*_fplc*Q1pZWCUyRlJCWUJWTjB0dkV5VFB1VUhKYXF5Z2xiQ3JPUXVJZ0ElMkZjcExHREh5YTBvUnU1MUlVdlRycm5rQnVZbUlPdU1XUVVNNHclMkZvZTFQTkp5S2l1TjUzVWxGaE1KJTJGbElOU0xlTUN3Wm5mazZJcFRMUHdFUnZldERaMEgzU3clM0QlM0Q.

https://github.com/Milsondepaz/como-instalar-intellij-no-ubuntu

https://blog.geekhunter.com.br/introducao-a-kotlin/

https://kotlinlang.org/docs/arrays.html#create-arrays

https://kotlinlang.org/docs/strings.html#multiline-strings

https://kotlinlang.org/docs/numbers.html#explicit-number-conversions

https://pt.stackoverflow.com/questions/277219/o-que-%C3%A9-flyway-e-quando-us%C3%A1-lo