Hello, Kotlin!

um overview prático sobre a linguagem e seu status atualmente

 

github.com/streppel

streppels@gmail.com

  • Atualmente trabalho na www.bornlogic.com, onde desenvolvo utilizando Kotlin e Go ✨

we're hiring! 🙏🏼

  1. 🤠 O quê, e porquê
  2. 📈 Status atual da linguagem
  3. ✨ Design (basics 101)
  4. 🚀 Indo além (basics 102)
  5. 💪🏼 Frameworks e recursos para buscar mais

O quê

Linguagem criada em 2011 pela JetBrains

Cross-Plataform

roda na JVM

Cross-Plataform

roda na JVM *

 

* também transpila(sic) pra javascript

 

Cross-Plataform

roda na JVM * **

 

* também transpila(sic) pra javascript

** também compila para binário nativo (kotlin native)

Interoperável

Interoperável

Aproveite anos de maturidade do ecossistema Java - sem precisar escrever Java!

Interoperável

import java.util.*

Fácil de aprender

Fácil de aprender

  • Concisa              

Fácil de aprender

  • Concisa              
  • OOP

Fácil de aprender

  • Concisa              
  • OOP
  • FP

Fácil de aprender

  • Concisa              
  • OOP
  • FP
  • Null-safe* 🤭

OPEN SOURCE 🗣

Porquê

 

  1. Expressividade

 

  1. Expressividade
  2. Escalabilidade

 

  1. Expressividade
  2. Escalabilidade
  3. Interoperabilidade

Stats

A grande maioria ainda é novata

A quantidade de gente que vem usando em prod vem aumentando

Mas bastante gente ainda não colocou em prod por...

Android e JVM dominam o uso

E os tipos de aplicações são de usos bem espalhados 👨🏼‍💻👩🏼‍💻

BASICS 101

fun main(args: Array<String>) {
    println("Hello World!")
}
fun main(args: Array<String>) {
    println("Hello World!")
}

fun

 

fun main(args: Array<String>) {
    println("Hello World!")
}

 

nome: Tipo

fun main(args: Array<String>) {
    println("Hello World!")
}

OPTIONAL SEMI COLONS

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

 

função retorna um Int

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

 

agora prestem atenção nos parâmetros...

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

 

EPA, algo novo aqui

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

 

DEFAULT PARAMETERS!

fun main(args: Array<String>) {
    println("Hello World!")
}

 

agora voltando pro main, só pra explicar rapidinho

fun main(args: Array<String>) {
    println("Hello World!")
}

 

no exemplo do main não retornemos nada, é "void" por default (tipo Unit, na verdade)

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}
class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}

 

class

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}

 

constructor

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}

 

name já é um membro da classe

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}

 

greet() é um public method

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}

 

string interpolation

class Greeter(val name: String) {
    fun greet() {
        println("Hello, ${name}");
    }
}

fun main(args: Array<String>) {
    Greeter(args[0]).greet()
}

 

instanciamento sem new

val PI = 3.14

var x = 0
val PI = 3.14

var x = 0

 

val significa READ-ONLY

val PI = 3.14
PI = 123 // COMPILER ERROR 😡
var x = 0

 

val significa READ-ONLY

val PI = 3.14

var x = 0

 

var é mutável = pode ter seu valor atualizado

val PI = 3.14

var x = 0
x = 213 // OK 👌🏼

 

var é mutável = pode ter seu valor atualizado

BASICS 102

val b = BigDecimal(500)

// agora eu preciso da porcentagem de "b"
val b = BigDecimal(500)

// the java way of living
class BigDecimalUtils {
  fun percentage(b: BigDecimal, p: Int): BigDecimal {
    return b.multiply(BigDecimal(p))
            .divide(BigDecimal(1000)
  }
}

 

class BigDecimalUtils

val b = BigDecimal(500)

// the java way of living
class BigDecimalUtils {
  fun percentage(b: BigDecimal, p: Int): BigDecimal {
    return b.multiply(BigDecimal(p))
            .divide(BigDecimal(1000)
  }
}

 

method percentage

val b = BigDecimal(500)

// the java way of living
class BigDecimalUtils {
  fun percentage(b: BigDecimal, p: Int): BigDecimal {
    return b.multiply(BigDecimal(pctg)).divide(BigDecimal(1000)
  }
}

can we do better?

val b = BigDecimal(500)

fun BigDecimal.percent(p: Int) {

}

 

EXTENDEMOS BigDecimal

val b = BigDecimal(500)

fun BigDecimal.percent(p: Int) {
  return this.multiply(BigDecimal(p))
             .divide(BigDecimal(1000)
}

 

Essa é a classe BigDecimal do JAVA

val b = BigDecimal(500)

fun BigDecimal.percent(p: Int) =
   this.multiply(BigDecimal(p))
       .divide(BigDecimal(1000)

 

Single-Expression functions

val b = BigDecimal(500)

fun BigDecimal.percent(p: Int) =
   this.multiply(BigDecimal(p))
       .divide(BigDecimal(1000)

 

não precisamos declarar retorno de funções com retorno óbvio, o compilador entende!

 

E funciona com tipos nativos também!

fun Int.percentOf(t: BigDecimal) = 
  t.multiply(BigDecimal(this))
   .divide(BigDecimal(1000))

val popcornPrice = BigDecimal(7.0)

7.percentOf(popcornPrice)

 

🤯

fun Int.percentOf(t: BigDecimal) = 
  t.multiply(BigDecimal(this))
   .divide(BigDecimal(1000))

val popcornPrice = BigDecimal(7.0)

7.percentOf(popcornPrice)

 

Infix Notation

infix fun Int.percentOf(t: BigDecimal) = 
  t.multiply(BigDecimal(this))
   .divide(BigDecimal(1000))

val popcornPrice = BigDecimal(7.0)

7.percentOf(popcornPrice)

 

Infix Notation

infix fun Int.percentOf(t: BigDecimal) = 
  t.multiply(BigDecimal(this))
   .divide(BigDecimal(1000))

val popcornPrice = BigDecimal(7.0)

7 percentOf popcornPrice

 

Agora podemos, se quisermos, chamar sem . e sem parêntesis

class Money {
   // ... implements fun sum(m: Money)
}

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}
// a and b são do tipo Money
val valorTotal = a + b

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}
// a and b são do tipo Money
val valorTotal = a + b

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}
// a and b são do tipo Money
val valorTotal = a + b

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}
// a and b são do tipo Money
val valorTotal = a + b

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}
// a and b são do tipo Money
val valorTotal = a + b

 

Operator Overloading

class Money {
   // ... implements fun sum(m: Money)
}
operator fun Money.plus(m: Money) {
  return this.sum(m)
}
// a and b são do tipo Money
val valorTotal = a + b

 

Operator Overloading

fun getResult() Pair<Int, Int> {
  // .... 
}

 

Destructuring Declarations

fun getResult() Pair<Int, Int> {
  // .... 
}
//   int     int 
val (result, status) = getResult()

 

Destructuring Declarations

fun getResult() Pair<Int, Int> {
  // .... 
}
//   int     int 
val (result, status) = getResult()

//  Pair<int, Int>
val result = getResult()

 

Destructuring Declarations

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, {value -> value.endsWith(".com")})

 

High Order Functions

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, {value -> value.endsWith(".com")})

 

função por

parâmetro

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, {value -> value.endsWith(".com")})

 

função lambda passada como parâmetro

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, {value -> value.endsWith(".com")})

 

parâmetro da lambda

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, {value -> value.endsWith(".com")})

 

corpo da lambda

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, {value -> value.endsWith(".com")})

 

parâmetros únicos são automaticamente identificados e chamados de "it"

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users, { it.endsWith(".com")})

 

parâmetros únicos são automaticamente identificados e chamados de "it"

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users) { 
  it.endsWith(".com")
}

 

passando a lambda por fora dos parêntesis

fun findEmails(users: List<User>,
        pred: (String) -> (Boolean)): List<emails> {
  // .... 
}
findEmails(users) { 
  it.endsWith(".com")
}

 

isso é possível quando a função chamada recebe outra função como último parâmetro

val users = fetchUsers()

users.filter { it.email.endsWith(".com") }
     .sortedBy { it.id }
     .map { Pair(it.name, it.email)}

 

exemplos como esses são possíveis

 

 

Uma (rapidíssima) introdução a Coroutines

 

Coroutines

 

  • Stackless

 

Coroutines

 

  • Stackless
  • Cooperative Multitasking

 

Coroutines

 

  • Stackless
  • Cooperative Multitasking

(non-preventive)

 

Coroutines

 

  • Stackless
  • ​Cooperative Multitasking
  • Rodam em threads

 

Coroutines

 

  • Stackless
  • ​Cooperative Multitasking
  • Rodam em threads
    • ​cada uma pode ter a sua, ou podem rodar em uma única
val sum = AtomicLong()
for (i in 0..100_000) {
    GlobalScope.launch {
        sum.addAndGet(1)
    }
}
val sum = AtomicLong()
for (i in 0..100_000) {
    GlobalScope.launch {
        sum.addAndGet(1)
    }
}

 

escopo da coroutine (global)

val sum = AtomicLong()
for (i in 0..100_000) {
    GlobalScope.launch {
        sum.addAndGet(1)
    }
}

 

launch inicia uma nova coroutine

val sum = AtomicLong()
for (i in 0..100_000) {
    GlobalScope.launch {
        sum.addAndGet(1)
    }
}

 

100 mil coroutines, hein?

 

exemplo com medição de tempo

 

169 milliseconds pra executar 100 mil coroutines

 

Tente fazer o mesmo usando Threads em qualquer linguagem e veja o que vai acontecer

 

Temos muito a aprender sobre coroutines. Channels, context, dispatchers... aconselho estudar

 

E pra finalizar, flow

 

Flow é para a programação reativa o que coroutines são para programação assíncrona

fun <T> Flow<T>.delayASecond() = flow {
    collect { value -> // collect from the original flow
        delay(1000)    // delay 1 second
        emit(value)    // emit value to the resulting flow
    }
}

 

é a interface para desenvolvermos cold streams

fun <T> Flow<T>.delayASecond() = flow {
    collect { value -> // collect from the original flow
        delay(1000)    // delay 1 second
        emit(value)    // emit value to the resulting flow
    }
}

 

resolve back-pressure por detrás dos panos usando suspensions

fun <T> Flow<T>.delayASecond() = flow {
    collect { value -> // collect from the original flow
        delay(1000)    // delay 1 second
        emit(value)    // emit value to the resulting flow
    }
}

 

resolve back-pressure por detrás dos panos usando suspensions

 

E é isso galera 😎

    Leiam a documentação e se divirtam!

https://kotlinlang.org/docs/reference/

 

github.com/streppel

streppels@gmail.com

Made with Slides.com