Skip to content

Latest commit

 

History

History
578 lines (454 loc) · 18.8 KB

Kotlin-Fundamentals.md

File metadata and controls

578 lines (454 loc) · 18.8 KB

Kotlin Fundamentals - PagSeguro Training Program

Useful Links

Kotlin

  • Home page is https://kotlinlang.org
  • JetBrains criou e mantém a linguagem
  • Oficialmente endossado pelo Google como uma linguagem de desenvolvimento Android
  • Fornece null safety no nível do compilador
  • Estaticamente tipado por padrão
    • Significa que o tipo de cada expressão em um programa é conhecido em tempo de compilação
    • O compilador valida se os métodos e campos que você está tentando acessar existem nos objetos que você está usando.
    • Vantagens de Linguagens Estaticamente Tipadas
  • Executa na JVM --> Interoperabilidade com Java

Instalando e Rodando Kotlin

SDKMAN

  • sdk install kotlin

Rodando Kotlin

  1. Crie um aplicativo simples em Kotlin que exiba "Hello, World!". Em seu editor favorito, crie um novo arquivo chamado hello.kt com as seguintes linhas:
fun main() {
    println("Hello, World!")
}
  1. Compile a aplicação usando o comando: $ kotlinc hello.kt -include-runtime -d hello.jar

A opção -d indica o caminho de saída para arquivos de classe gerados, que podem ser um diretório ou um arquivo .jar. A opção -include-runtime torna o arquivo .jar resultante independente e executável, incluindo Kotlin runtime library.

  1. Rodando a aplicação: java -jar hello.jar

Para mais informações conferir: Kotlin command-line compiler

Kotlin Basics

Functions e Variables

Variables

  • Declarando uma variavel: val s: String

  • val vs var

    • var é uma variavel (mutable)
      • O valor de variáveis declaradas com var pode ser alterado.
    • val é um valor (immutable)
      • val não pode ser reatribuído depois de inicializado
    • Por padrão, você deve se esforçar para declarar todas as variáveis em Kotlin com a palavra-chave val.
    • [WARNING!!] Embora variaveis declaradas com var permitam que seu valor seja alterado, seu tipo é fixo.
      • O exemplo abaixo não irá compilar
        var answer = 42
        answer = "no answer"
  • Variáveis em kotlin são non-null por padrão

  • Para declarar uma variavel que pode assumir null é decessário declara-lá como nullable utilizando ?

    • Ex: val name: String?
  • The !! operator

    • O operador de asserção não nulo (!!) converte qualquer valor em um tipo não nulo e lança uma exceção se o valor for nulo. Você pode escrever b!!, e isso retornará um valor não nulo de b (por exemplo, uma String em nosso exemplo) ou lançará um NPE se b for nulo:
    val l = b!!.length
    

Functions

fun main(args: Array<String>) {
    println("Hello, world!")
}
  • fun keyword é usada para declarar uma função
  • tipo de parâmetro é escrito após seu nome (ex: args: Array<String>)
fun max(a: Int, b: Int): Int {
    return if (a > b) a else b
}
  • A declaração da função começa com a keyword fun, seguida pelo nome da função (no caso do exemplo é max). Depois, é seguido pela lista de parâmetros entre parênteses. O tipo de retorno vem após a lista de parâmetros, separado dela por dois pontos (:).

  • Default parameter Values

    • Em Kotlin, você pode especificar valores padrão para parâmetros em uma declaração de função.
      fun max(a: Int = 0, b: Int = 0): Int {
          return if (a > b) a else b
      }
      

Verificando Tipos

Use is.

val age = 25
if (age is String) { 
    println("Age is a String")
}

if (age is Int) { 
    println("Age is an In")
}

Expressions - if else

fun max(a: Int, b: Int): Int = if (a > b) a else b
fun max(a: Int, b: Int): Int {

    if(a > b) {
        return a
    } else {
        return b
    }

} 

Elvis Operator

  • Quando você tem uma referência anulável, b, você pode dizer "se b não for nulo, use-o, caso contrário, use algum valor não nulo":
    val l: Int = if (b != null) b.length else -1
  • Uma alternativa ao if else para verificar null é a utilização do Elvis Operator
    val l = b?.length ?: -1
    • se a expressão à esquerda de ?: não for nula, o operador Elvis a devolve, caso contrário devolve a expressão à direita.

Enums e when

Enums

  • Enums em Kotlin são tipos de dados que contêm um conjunto de constantes. Enums são definidos adicionando o modificador enum na frente de uma classe, conforme mostrado abaixo.
    • Em Kotlin, Enums são classes.
enum class Months{
    January,
    February,
    March
}
fun main(args: Array<String>) {

    println(Months.January) //prints January
    println(Months.values().size) //prints 3
    println(Months.valueOf("March")) //prints March

    for (enum in Months.values()) {
        println(enum.name)
    }

    println(Months.valueOf("Mar")) //throws java.lang.IllegalArgumentException: No enum constant Months.Mar
}
  • values() retorna as constantes enum na forma de um array sobre o qual podemos iterar para recuperar cada constante enum.
  • valueOf() é usado para buscar uma constante enum usando uma String como argumento.

Inicialização de Enums

Enums podem ser inicializadas usando construtores primários, conforme mostrado abaixo.

enum class Months(var shorthand: String) {
    January("JAN"),
    February("FEB"),
    March("MAR");
}

Mais sobre Enums

Kotlin Enum Class

O operador when

  • Use when quando um determinado bloco de código precisa ser executado quando alguma condição é cumprida.
var num = 10
    when (num) {
        0 -> print("value is 0")
        5 -> print("value is 5")
        else -> {
            print("value is neither 0 nor 5") //this gets printed.
        }
    }
  • O operador when corresponde ao argumento com todas as ramificações. Se não corresponder a nenhum, a instrução else será executada.
  • when operador pode ser usado para retornar valores também, semelhante a if else.
var valueLessThan100 = when(101){
        in 1 until 101 -> true
        else -> {
            false
        }
    }
    
    print(valueLessThan100) //false

Mais sobre when operator

Kotlin when expression

Loops - while e for

for Loop

  • O loop for é usado para iterar sobre uma lista de itens com base em determinadas condições.
  • A seguir está a implementação de loops for em Kotlin para imprimir números de 0 a 5.
for (i in 0..5) {
    print(i)
}
  • Outro caso para você, onde iteramos sobre uma matriz usando o loop for-in.
val items = listOf(10, 20, 30, 40)

    for (i in items)
        println("value is $i")

//Following is printed to the console.
value is 10
value is 20
value is 30
value is 40

Mais sobre for loop

Kotlin for Loop

Kotlin while loop

while loop – consiste em um bloco de código e uma condição. Em primeiro lugar, a condição é avaliada e, se for verdadeira, execute o código dentro do bloco. Ele se repete até que a condição se torne falsa porque toda vez que a condição é verificada antes de entrar no bloco.

while(condition) {
           // code to run
}

i=0
while(i<=5) {
    print(i)
    i++
}

//prints 012345

Mais sobre while loop

Kotlin while loop

Pacotes (Packages)

Classes, Objects, Interface e Properties

Classes

As classes em Kotlin são declaradas usando a keyword class:

class Person { /*...*/ }

Construtor

Uma classe em Kotlin pode ter um construtor primário e um ou mais construtores secundários.

  • Primary Constructor

    • O construtor primário não pode conter nenhum código.
      class Person(firstName: String) { /*...*/ }
      
      class Person(val firstName: String, val lastName: String, var age: Int)
  • Exercício: Ler sobre o bloco init (Init Blocks Kotlin Constructors)

  • Secondary Constructor

    • Uma classe também pode declarar construtores secundários, que são prefixados com construtor
    • Construtores secundários permitem a inicialização de variáveis e também fornecem alguma lógica para a classe
      class Pet {
          constructor(owner: Person) {
              owner.pets.add(this) 
          }
      }
      
      class Add {
          constructor(a: Int, b:Int) {
              var c = a + b
              println("The sum of numbers 5 and 6 is: ${c}")
          }
      }
  • Exercício: Ler sobre algumas formas de se utilizar Secondary Constructor (Init Blocks Kotlin Constructors)

Criando instâncias de classes

val invoice = Invoice()

val customer = Customer("Joe Smith")

Companion objects

Em Kotlin, se você deseja escrever uma função ou qualquer membro da classe que possa ser chamado sem ter a instância da classe, você pode escrever o mesmo como um membro de um objeto companheiro dentro da classe. Assim, ao declarar o companion object, você pode acessar os membros da classe apenas pelo nome da classe (sem criar explicitamente a instância da classe).

class SomeNiceClass {

    companion object CompanionObject {

    }
}
val obj = SomeNiceClass.CompanionObject
class ToBeCalled {
    companion object Test {
        fun callMe() = println("You are calling me :)")
    }
}
fun main(args: Array<String>) {
    ToBeCalled.callMe()
}

Mais sobre Companion Objects

Kotlin Companion Objects

Herança

A herança é um dos recursos mais importantes na programação orientada a objetos. A herança permite a reutilização de código, permite que todos os recursos de uma classe existente (classe base) sejam herdados por uma nova classe (classe derivada). Além disso, a classe derivada também pode adicionar alguns recursos próprios.

open class baseClass (x:Int ) {
      ..........
}
class derivedClass(x:Int) : baseClass(x) {
     ...........
}

Em Kotlin, todas as classes são final por padrão. Para permitir que a classe derivada herde da classe base, devemos usar a palavra-chave open na frente da classe base.

//base class
open class BaseClass{
    val name = "Augusto"
    fun printName(){
        println("Base Class")
    }
}
//derived class
class DerivedClass: BaseClass() {
    fun printNameWithInfo(info: String) {
        println("${name} + ${info}" ) //inherit **name** property
    }
}
fun main(args: Array<String>) {
    val derived: DerivedClass = DerivedClass()
    derived.printName()         // inheriting the  base class function
    derived.printNameWithInfo("Developer")         // calling derived class function

    val baseclass = DerivedClass() // What is going to happen here?
}

Interface

  • Declarando uma interface:

    interface Clickable {
        fun click()
    }
  • Implementando uma interface

    class Button : Clickable {
        override fun click() = println("I was clicked")
    }

Open, final, e abstract modifiers

Estudo recomendado: Ler o Cap 4. Classes, objects, and interfaces de JEMEROV, Dmitry and ISAKAVA, Svetlana. Kotlin in Action, 2017.

Exceptions

  • Uma função pode ser concluída de maneira normal ou lançar uma exceção se ocorrer um erro. O chamador da função pode capturar essa exceção e processá-la.
if (percentage !in 0..100) {
    throw IllegalArgumentException(
        "A percentage value must be between 0 and 100: $percentage")
}

try, catch e finally

  • Para lidar com exceções, usamos o try-catch block.
  • O pedaço de código que tem a possibilidade de dar uma exceção é definido dentro do bloco try. A exceção é capturada dentro do bloco catch. Independentemente do que acontece, o bloco finally é sempre executado.
fun main(args: Array<String>) {
    try {

        var a = 0
        var x = 7 / a

        val v = "Journaldev.com"
        v.toInt()

    } catch (e: ArithmeticException) {
        println("Arthimetic Exception")
    } catch (e: Exception) {
        println("Exception occured. To print stacktrace use e")
    } finally {
        println("Finally. It's over")
    }
}

finally

  • Exercício: Busquem na internet os casos de uso do block finally

Collections and Arrays

val set = hashSetOf(1, 7, 53)
val list = arrayListOf(1, 7, 53)
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
  • Exercício: Qual a diferença entre as estruturas set, list e map em Kotlin?

Data classes

  • Não é incomum criar classes cujo objetivo principal seja armazenar dados. Em tais classes, algumas funcionalidades padrão e algumas funções utilitárias são frequentemente deriváveis mecanicamente a partir dos dados. Em Kotlin, elas são chamadas de classes de dados e são marcadas com dados:
data class User(val name: String, val age: Int)
  • O compilador cria automaticamente os seguintes membros de todas as propriedades declaradas no construtor primário:

- equals()/hashCode() pair

  • toString() no formato "User(name=John, age=42)"

  • componentN() funções correspondentes às propriedades em sua ordem de declaração.

  • copy() function

  • Para garantir consistência e comportamento significativo do código gerado, as data classes devem atender aos seguintes requisitos:

    • O construtor primário precisa ter pelo menos um parâmetro.

    • Todos os parâmetros do construtor primário precisam ser marcados como val ou var.

    • As classes de dados não podem ser abstratas, abertas, sealed ou internas.

Mais sobre Data Classes loop

Kotlin Data classes

Vantagens de Linguagens Estaticamente Tipadas

A seguir estão alguns dos benefícios da tipagem estática:

  • Desempenho— Chamar métodos é mais rápido porque não há necessidade de descobrir em tempo de execução qual método precisa ser chamado.
  • Confiabilidade— O compilador verifica a exatidão do programa, portanto, há menos chances de travamentos em tempo de execução.
  • Manutenibilidade— Trabalhar com código desconhecido é mais fácil porque você pode ver com que tipo de objetos o código está trabalhando.
  • Suporte a ferramentas— A tipagem estática permite refatorações confiáveis, preenchimento de código preciso e outros recursos do IDE.

Conceitos de Programação Funcional

  • First-class functions— Você trabalha com funções (partes de comportamento) como valores. Você pode armazená-los em variáveis, passá-los como parâmetros ou retorná-los de outras funções.
  • Immutability— Você trabalha com objetos imutáveis, o que garante que seu estado não possa mudar após sua criação.
  • No side effects— Você usa funções puras que retornam o mesmo resultado com as mesmas entradas e não modificam o estado de outros objetos ou interagem com o mundo exterior.

Null-safety

O que significa non-nullable por padrão?

fun main() {
  word: String
  print(word); // illegal

  word = 'Hello';
  print(word); // legal
}

Como você pode ver acima, uma variável não anulável por padrão significa que toda variável declarada normalmente não pode ser nula. Conseqüentemente, qualquer operação que acesse a variável antes de ser atribuída é ilegal. Além disso, atribuir null a uma variável não anulável também não é permitido:

fun main() {
  word: String
  word = null // forbidden
  world = 'World!' // allowed
}

Como isso me ajuda?

Se uma variável não for non-nullable, você pode ter certeza de que ela nunca será nula. Por isso, você nunca precisa verificar antes.

number: Int = 4;

fun main() {
  if (number == null) return; // redundant

  sum: Int = number + 2; // allowed because number is also non-nullable
}