Kotlin: Variáveis e Sistema de Tipos do Kotlin em…

Última atualização: 2026-08-26

O sistema de tipos do Kotlin não é uma cópia do Java — ele elimina conversões forçadas com smart casts, constrói uma álgebra de tipos completa com Any/Unit/Nothing e faz o sistema de tipos trabalhar a seu favor, não contra você.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) Problema: O Inferno das Conversões de Tipo do Java

Bob estava mantendo código Java do OrderProcessor, e conversões forçadas estavam por toda parte: (Order), (String). Faltar uma única verificação instanceof provocava uma ClassCastException — 3-5 incidentes em produção por trimestre.

(2) Solução: Smart Cast do Kotlin

KOTLIN
// Java: verboso e propenso a erros
if (obj instanceof Order) {
    Order order = (Order) obj;  // Precisa converter novamente
    System.out.println(order.getId());
}

// Kotlin: smart cast elimina a conversão
if (obj is Order) {
    println(obj.id)  // Nenhuma conversão necessária!
}

O compilador Kotlin insere automaticamente conversões seguras após verificações is. Bob nunca mais escreveu uma conversão manual.


3. Tipos Básicos

(1) Tipos Numéricos

O Kotlin não expõe os tipos primitivos do Java (int/double) — tudo é objeto — mas o compilador os otimiza para primitivos na JVM.

KOTLIN
val intValue: Int = 42          // 32 bits
val longValue: Long = 42L       // 64 bits
val doubleValue: Double = 3.14  // 64 bits IEEE 754
val floatValue: Float = 3.14f   // 32 bits IEEE 754

// Sublinhado para legibilidade
val million = 1_000_000
val creditCard = 1234_5678_9012_3456L
val bytes = 0b11010010_01101001
Tipo Bits Intervalo Equivalente Java
Byte 8 -128 ~ 127 byte
Short 16 -32768 ~ 32767 short
Int 32 -2³¹ ~ 2³¹-1 int
Long 64 -2⁶³ ~ 2⁶³-1 long
Float 32 IEEE 754 float
Double 64 IEEE 754 double

(2) Tipos Não Numéricos

KOTLIN
val bool: Boolean = true
val char: Char = 'A'
val str: String = "OrderProcessor"

// Caractere NÃO é um número no Kotlin
// val code: Int = 'A'  // ERRO
val code: Int = 'A'.code  // OK: 65
val charFromCode: Char = 65.toChar()  // OK: 'A'

4. Smart Cast

O compilador Kotlin converte automaticamente uma variável para um tipo mais específico após uma verificação de tipo — sem conversão manual necessária.

(1) Smart Cast Básico

KOTLIN
fun process(input: Any) {
    // Após verificação 'is', o compilador faz smart cast
    if (input is String) {
        println(input.length)  // Smart cast para String
    }

    // Após verificação '!is'
    if (input !is String) return
    println(input.length)  // Smart cast para String
}

// Smart cast com when
fun describe(obj: Any): String = when (obj) {
    is String -> "String: ${obj.length} caracteres"     // Smart cast
    is Int -> "Int: ${obj.dec()}"                        // Smart cast
    is List<*> -> "Lista: ${obj.size} itens"             // Smart cast
    else -> "Desconhecido"
}

(2) Limitações do Smart Cast

KOTLIN
// AVISO: smart cast pode não funcionar com var ou getters personalizados
var obj: Any = "Hello"
if (obj is String) {
    // obj pode ser alterado entre a verificação e o uso
    // println(obj.length)  // AVISO em alguns casos
}

// Abordagem segura: usar val local
val safeObj = obj
if (safeObj is String) {
    println(safeObj.length)  // OK - garantido estável
}

(3) Conversão Java vs Smart Cast Kotlin

Dimensão Java Kotlin
Sintaxe (Type)obj Automático
Segurança Pode causar CCE em tempo de execução Garantia em tempo de compilação
Tamanho do código Verificação + conversão (dois passos) Verificação = conversão (um passo)
Segurança nula Pode causar NPE Lida automaticamente com anuláveis

5. Conversões Explícitas de Tipos

O Kotlin não suporta conversões de estreitamento implícitas (ex.: LongInt) — todas as conversões devem ser chamadas explicitamente.

(1) Funções de Conversão

KOTLIN
val longVal: Long = 42L
val intVal: Int = longVal.toInt()    // Estreitamento explícito
val doubleVal: Double = 3.99
val intFromDouble: Int = doubleVal.toInt()  // Trunca: 3

// String para número
val parsed: Int = "42".toInt()
val parsedDouble: Double = "3.14".toDouble()

// Número para string
val strVal: String = 42.toString()

(2) Por Que Não Conversões Implícitas?

KOTLIN
// Isso perderia dados silenciosamente - o Kotlin impede
val longValue: Long = 2_147_483_648L  // Excede Int.MAX_VALUE
// val intValue: Int = longValue  // ERRO DE COMPILAÇÃO

// Deve ser explícito sobre a potencial perda de dados
val intValue: Int = longValue.toInt()  // OK, mas pode causar overflow
Tipo de Conversão Java Kotlin
Alargamento (Int→Long) Implícito Explícito toLong()
Estreitamento (Long→Int) Implícito (arriscado) Explícito toInt()
String→Número Integer.parseInt() "42".toInt()
Número→String "" + 42 42.toString()

6. Três Tipos de Topo

(1) Diagrama de Hierarquia de Tipos

100%
classDiagram
    Any --> String
    Any --> Int
    Any --> Order
    Any --> Unit
    Any --> Nothing
    class Any {
        +equals()
        +hashCode()
        +toString()
    }
    class Unit {
        <<singleton>>
    }
    class Nothing {
        <<nunca retorna>>
    }

(2) Any: O Pai de Todos os Tipos Não Nulos

KOTLIN
// Any é a raiz da hierarquia de tipos do Kotlin (como Object no Java)
val obj: Any = "Hello"
val obj2: Any = 42
val obj3: Any = Order("ORD-001", 299.99, "CONFIRMED")

// Any tem apenas 3 métodos: equals, hashCode, toString
// Use Any? para o tipo raiz anulável

(3) Unit: O "Sem Valor de Retorno" Tipado

KOTLIN
// Unit é como void no Java, mas é um tipo real
fun logOrder(order: Order): Unit {  // Retorno Unit é opcional
    println("Processando ${order.id}")
}

fun logOrder2(order: Order) {  // Equivalente - Unit inferido
    println("Processando ${order.id}")
}

// Unit é um singleton - pode ser usado como um valor
val unitValue: Unit = Unit

(4) Nothing: Nunca Alcança

KOTLIN
// Nothing significa "este código nunca retorna"
fun fail(message: String): Nothing {
    throw IllegalStateException(message)
}

// Útil para operador Elvis com exceções
val customer = order.customer ?: fail("Cliente obrigatório")

// Loop infinito também retorna Nothing
fun infiniteLoop(): Nothing {
    while (true) { /* nunca retorna */ }
}

(5) Comparação dos Três Tipos de Topo

Tipo Significado Caso de Uso Equivalente Java
Any Pai de todos os tipos não nulos Referências genéricas Object
Unit Sem valor de retorno significativo Funções com efeitos colaterais void
Nothing Nunca alcança o fim Lançar exceções / loops infinitos Nenhum

7. Apelidos de Tipo (typealias)

KOTLIN
// Simplificar assinaturas de tipo complexas
typealias OrderMap = Map<String, List<Order>>
typealias OrderProcessor = (List<Order>) -> List<String>
typealias USD = Double

// Uso
val ordersByCustomer: OrderMap = mapOf(
    "Alice" to listOf(Order("ORD-001", 299.99, "CONFIRMED"))
)

typealias Predicate<T> = (T) -> Boolean
val isHighValue: Predicate<Order> = { it.total > 10_000 }

// typealias NÃO cria novos tipos - apenas apelidos
val map: OrderMap = ordersByCustomer  // Mesmo tipo

8. Exemplo Completo: Processamento Tipado Seguro do OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - Processamento de Eventos Tipado Seguro
// Funcionalidade: Processar eventos de pedido com smart cast
// ============================================

typealias OrderId = String
typealias USD = Double

sealed class OrderEvent {
    data class Created(val orderId: OrderId, val total: USD) : OrderEvent()
    data class Paid(val orderId: OrderId, val amount: USD) : OrderEvent()
    data class Shipped(val orderId: OrderId, val trackingCode: String) : OrderEvent()
    data class Cancelled(val orderId: OrderId, val reason: String) : OrderEvent()
}

fun handleEvent(event: OrderEvent): String = when (event) {
    is OrderEvent.Created -> {
        // Smart cast: event.orderId e event.total disponíveis
        val threshold = 10_000
        val priority = if (event.total > threshold) "VIP" else "STANDARD"
        "Pedido ${event.orderId} criado (\$${event.total} USD) -> $priority"
    }
    is OrderEvent.Paid -> {
        val tax = event.amount * 0.08
        "Pedido ${event.orderId} pago: \$${event.amount} USD (imposto: \$$tax USD)"
    }
    is OrderEvent.Shipped ->
        "Pedido ${event.orderId} enviado: ${event.trackingCode}"
    is OrderEvent.Cancelled ->
        "Pedido ${event.orderId} cancelado: ${event.reason}"
}

fun main() {
    val events: List<OrderEvent> = listOf(
        OrderEvent.Created("ORD-001", 299.99),
        OrderEvent.Created("ORD-002", 15_000.00),
        OrderEvent.Paid("ORD-001", 299.99),
        OrderEvent.Shipped("ORD-001", "TRK-ABC123"),
        OrderEvent.Cancelled("ORD-003", "Solicitação do cliente")
    )

    events.forEach { event ->
        val result = handleEvent(event)
        println(result)
    }

    // Verificação de tipo com smart cast
    val anyEvent: Any = events[0]
    if (anyEvent is OrderEvent.Created) {
        println("\nSmart cast funciona: ${anyEvent.orderId} custa \$${anyEvent.total} USD")
    }
}

Saída:

TEXT 📖 Somente leitura
Pedido ORD-001 criado ($299.99 USD) -> STANDARD
Pedido ORD-002 criado ($15000.0 USD) -> VIP
Pedido ORD-001 pago: $299.99 USD (imposto: $23.999200000000002 USD)
Pedido ORD-001 enviado: TRK-ABC123
Pedido ORD-003 cancelado: Solicitação do cliente

Smart cast funciona: ORD-001 custa $299.99 USD

9. Exemplos práticos rápidos

▶ Exemplo: val e var com inferência de tipo

KOTLIN
// Inferência de tipo: variáveis não precisam de anotação de tipo explícita
val orderId = "ORD-001"           // String
val total = 299.99                // Double
val isPaid = true                 // Boolean

// var com mudança de valor
var status = "PENDING"
status = "CONFIRMED"

// Tipos explícitos quando necessário
val explicitInt: Int = 42
val explicitDouble: Double = 3.14
val explicitString: String = "Olá"

println("orderId: $orderId (${orderId::class.simpleName})")
println("total: $total (${total::class.simpleName})")
println("status: $status")

Saída:

TEXT 📖 Somente leitura
orderId: ORD-001 (String)
total: 299.99 (Double)
status: CONFIRMED

▶ Exemplo: Tipos numéricos básicos

KOTLIN
// Tipos inteiros
val byteVal: Byte = 127                  // -128 a 127
val shortVal: Short = 32_000              // -32768 a 32767
val intVal: Int = 2_000_000_000           // mais comum
val longVal: Long = 9_000_000_000L        // notar sufixo L

// Tipos decimais
val floatVal: Float = 3.14f               // notar sufixo f
val doubleVal: Double = 3.14159265358979

// Conversões explícitas
val intFromDouble = doubleVal.toInt()     // 3 (truncado)
val doubleFromInt = intVal.toDouble()     // 2000000000.0

// Underscores em valores numéricos (legibilidade)
val milhao = 1_000_000
val pi = 3.14159_26535_89793

println("intFromDouble: $intFromDouble")
println("doubleFromInt: $doubleFromInt")
println("milhao: $milhao")

Saída:

TEXT 📖 Somente leitura
intFromDouble: 3
doubleFromInt: 2.0E9
milhao: 1000000

▶ Exemplo: String com métodos comuns

KOTLIN
val order = "ORD-001"
// Comprimento, acesso, fatiamento
println("Length: ${order.length}")
println("First 3: ${order.substring(0, 3)}")
println("Contains 001: ${order.contains("001")}")

// Templates e expressões
val total = 299.99
val msg = "Pedido $order: \$$total USD"
println(msg)

// Multilinha com trimMargin
val report = """
    |Pedido: $order
    |Total: $total
    |Status: PENDING
""".trimMargin()
println(report)

// Comparação de strings
val a = "Alice"
val b = "alice"
println("Igual: ${a == b}")        // false
println("Igual ignorando caso: ${a.equals(b, ignoreCase = true)}")  // true

Saída:

TEXT 📖 Somente leitura
Length: 7
First 3: ORD
Contains 001: true
Pedido ORD-001: $299.99 USD
Pedido: ORD-001
Total: 299.99
Status: PENDING

Igual: false
Igual ignorando caso: true

▶ Exemplo: Any, Unit e Nothing

KOTLIN
// Any: tipo raiz - todos os outros tipos herdam dele
fun describe(value: Any): String = "${value::class.simpleName}: $value"
println(describe(42))
println(describe("Olá"))
println(describe(listOf(1, 2, 3)))

// Unit: como void - tipo com valor único ()
fun logMessage(msg: String): Unit {
    println("[LOG] $msg")
}
logMessage("Pedido processado")

// Nothing: função "nunca retorna"
fun failWith(message: String): Nothing {
    throw IllegalStateException(message)
}

val x: Int = try {
    "não é um número".toInt()
} catch (e: NumberFormatException) {
    failWith("Entrada inválida: $e")
}

Saída:

TEXT 📖 Somente leitura
Int: 42
String: Olá
List: [1, 2, 3]
[LOG] Pedido processado
Exception: Entrada inválida: ...

▶ Exemplo: Conversões de tipo explícitas

KOTLIN
// Sem conversão implícita entre números - deve chamar .toXxx() explicitamente
val intVal: Int = 42
val longVal: Long = intVal.toLong()
val doubleVal: Double = intVal.toDouble()
val stringVal: String = intVal.toString()

// String para número
val parsed = "123".toInt()
val parsedDouble = "3.14".toDouble()

// Tipo para tipo seguro
val maybeInt: Any = "123"
val safeInt = (maybeInt as? Int) ?: 0    // 0 se não for Int
val unsafeInt = maybeInt as Int           // lança ClassCastException se falhar

println("long: $longVal")
println("double: $doubleVal")
println("string: $stringVal")
println("parsed: $parsed (${parsed::class.simpleName})")
println("safeInt: $safeInt (já que maybeInt é String)")

Saída:

TEXT 📖 Somente leitura
long: 42
double: 42.0
string: 42
parsed: 123 (Int)
safeInt: 0 (já que maybeInt é String)

▶ Exemplo: typealias para apelidos

KOTLIN
// Define apelidos para tipos complexos
typealias OrderId = String
typealias OrderMap = Map<OrderId, Double>
typealias OrderHandler = (OrderId, Double) -> String

// Uso
val prices: OrderMap = mapOf(
    "ORD-001" to 299.99,
    "ORD-002" to 1500.00
)

// Função que aceita apelido
val formatPrice: OrderHandler = { id, price ->
    "$id: \$$price USD"
}

// Chamada
prices.forEach { (id, price) ->
    println(formatPrice(id, price))
}

Saída:

TEXT 📖 Somente leitura
ORD-001: $299.99 USD
ORD-002: $1500.0 USD

▶ Exemplo: late e lazy

KOTLIN
// late: inicialização tardia mutável
lateinit var config: String

fun initializeConfig() {
    config = "produção"
}

fun main() {
    initializeConfig()
    println("Config: $config")
}

// lazy: inicialização no primeiro acesso
val expensiveValue: String by lazy {
    println("Calculando valor caro...")
    "resultado-${System.currentTimeMillis()}"
}

println("Antes do acesso")
println("Valor: $expensiveValue")  // inicializado aqui
println("Valor: $expensiveValue")  // retorna o mesmo valor sem reinicializar

Saída:

TEXT 📖 Somente leitura
Config: produção
Antes do acesso
Calculando valor caro...
Valor: resultado-1700000000000
Valor: resultado-1700000000000

❓ Perguntas Frequentes

P: O Int do Kotlin e o int do Java são a mesma coisa? R: Na JVM, o Int do Kotlin compila para o int do Java (tipo primitivo), e faz auto-boxing para Integer quando um objeto é necessário. Desenvolvedores não precisam se preocupar com essa diferença.

P: Quando o smart cast não funciona? R: Quando uma variável é var e pode ser modificada concorrentemente, ou tem um getter personalizado, o compilador não pode garantir tipos consistentes entre a verificação e o uso — smart cast não será aplicado.

P: Qual é o uso prático do tipo Nothing? R: É usado principalmente para marcar funções que "nunca retornam" (ex.: lançamento de exceções), ajudando o compilador a entender que o código subsequente é inalcançável, o que habilita smart casts e uso do operador Elvis.

P: typealias cria um novo tipo? R: Não. typealias é apenas um apelido de tipo — o compilador o substitui pelo tipo original. Para wrappers verdadeiramente seguros de tipo, use inline class / value class.

P: Por que o Kotlin não suporta conversões implícitas de tipos? R: Porque conversões de estreitamento implícitas (ex.: Long→Int) perdem dados silenciosamente e são uma fonte comum de bugs. Conversões explícitas tornam os desenvolvedores cientes da potencial perda de dados.

P: Qual a diferença entre Any e Any?? R: Any é o pai de todos os tipos não nulos; Any? é o pai de todos os tipos (incluindo anuláveis). Any? = Any | null é o verdadeiro topo do sistema de tipos do Kotlin.


📖 Resumo


📝 Exercícios

  1. Iniciante (⭐): Declare variáveis de diferentes tipos (Int, Double, Boolean, String) e imprima seus tipos ::class. Dica: use val x = 42; println(x::class)
  2. Intermediário (⭐⭐): Escreva uma função que receba Any, usando smart cast para tratar String (imprimir comprimento) e Int (imprimir valor ao quadrado). Dica: is String / is Int
  3. Desafio (⭐⭐⭐): Use typealias e sealed classes para projetar um sistema de tipos de eventos de pedido, com when tratando exaustivamente todos os tipos de evento. Dica: consulte o exemplo completo da Seção 8

← Anterior | Próximo →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%