github.com/streppel
streppels@gmail.com
we're hiring! 🙏🏼
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
Fácil de aprender
Fácil de aprender
Fácil de aprender
OPEN SOURCE 🗣
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 👨🏼💻👩🏼💻
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!")
}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
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)
}
}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!
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 fun Int.percentOf(t: BigDecimal) =
t.multiply(BigDecimal(this))
.divide(BigDecimal(1000))
val popcornPrice = BigDecimal(7.0)
7.percentOf(popcornPrice)
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
Coroutines
Coroutines
(non-preventive)
Coroutines
Coroutines
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