Hello, Kotlin!
um overview prático sobre a linguagem e seu status atualmente



github.com/streppel
streppels@gmail.com

- Natan Streppel, desenvolvedor
- Já trabalhei nas empresas:
- Compasso (www.compasso.com.br)
- Contabilizei (www.contabilizei.com.br)
- Amo Kotlin (e atualmente estou me apaixonando por Go)



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

we're hiring! 🙏🏼

- 🤠 O quê, e porquê
- 📈 Status atual da linguagem
- ✨ Design (basics 101)
- 🚀 Indo além (basics 102)
- 💪🏼 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ê



- Expressividade

- Expressividade
- Escalabilidade

- Expressividade
- Escalabilidade
- 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



github.com/streppel
streppels@gmail.com
Hello, Kotlin
By Natan Streppel
Hello, Kotlin
- 648