O que e Type Alias em Kotlin?

Um type alias em Kotlin e um nome alternativo para um tipo existente. Ele nao cria um novo tipo, mas oferece uma forma mais legivel de referenciar tipos complexos ou longos. Declarado com a palavra-chave typealias, esse recurso melhora a legibilidade do codigo sem impacto em performance, pois o compilador substitui o alias pelo tipo original durante a compilacao.

Type aliases sao especialmente uteis quando voce trabalha com tipos genericos compostos, funcoes de ordem superior com assinaturas longas ou tipos de plataformas externas que possuem nomes pouco descritivos.

Sintaxe basica

A declaracao de um type alias e feita no nivel do arquivo (top-level), fora de classes ou funcoes:

typealias ListaDeUsuarios = List<Usuario>
typealias Callback<T> = (T) -> Unit
typealias MapaDeNotas = Map<String, List<Double>>

Apos a declaracao, voce pode usar o alias em qualquer lugar onde usaria o tipo original:

data class Usuario(val nome: String, val idade: Int)

typealias ListaDeUsuarios = List<Usuario>

fun exibirUsuarios(usuarios: ListaDeUsuarios) {
    usuarios.forEach { println("${it.nome} - ${it.idade} anos") }
}

fun main() {
    val lista: ListaDeUsuarios = listOf(
        Usuario("Ana", 28),
        Usuario("Carlos", 35)
    )
    exibirUsuarios(lista)
}

Exemplos praticos

Simplificando tipos de funcao

Tipos de funcao em Kotlin podem ficar extensos quando possuem varios parametros. Type aliases resolvem isso:

typealias Validador<T> = (T) -> Boolean
typealias Transformador<T, R> = (T) -> R
typealias GerenciadorDeErro = (Exception) -> Unit

fun <T> filtrar(lista: List<T>, validador: Validador<T>): List<T> {
    return lista.filter(validador)
}

fun <T, R> mapear(lista: List<T>, transformador: Transformador<T, R>): List<R> {
    return lista.map(transformador)
}

fun executarComTratamento(bloco: () -> Unit, onErro: GerenciadorDeErro) {
    try {
        bloco()
    } catch (e: Exception) {
        onErro(e)
    }
}

fun main() {
    val numeros = listOf(1, 2, 3, 4, 5, 6)

    val pares = filtrar(numeros) { it % 2 == 0 }
    println(pares) // [2, 4, 6]

    val textos = mapear(numeros) { "Numero $it" }
    println(textos) // [Numero 1, Numero 2, ...]

    executarComTratamento(
        bloco = { println("Executando operacao") },
        onErro = { println("Erro: ${it.message}") }
    )
}

Trabalhando com tipos genericos complexos

typealias ResultadoApi<T> = Pair<Int, T?>
typealias TabelaDePrecos = Map<String, Map<String, Double>>
typealias MatrizInteira = Array<IntArray>

fun buscarUsuario(id: Int): ResultadoApi<Usuario> {
    return if (id > 0) {
        200 to Usuario("Ana", 28)
    } else {
        404 to null
    }
}

fun criarTabelaPrecos(): TabelaDePrecos {
    return mapOf(
        "eletronicos" to mapOf(
            "notebook" to 4500.00,
            "smartphone" to 2800.00
        ),
        "livros" to mapOf(
            "kotlin-em-acao" to 89.90,
            "clean-code" to 75.00
        )
    )
}

fun criarMatriz(linhas: Int, colunas: Int): MatrizInteira {
    return Array(linhas) { IntArray(colunas) }
}

Type alias para inner classes e nested classes

class Repositorio {
    sealed class Resultado<out T> {
        data class Sucesso<T>(val dados: T) : Resultado<T>()
        data class Erro(val mensagem: String) : Resultado<Nothing>()
        data object Carregando : Resultado<Nothing>()
    }
}

// Sem alias: Repositorio.Resultado.Sucesso<Usuario>
// Com alias:
typealias RepoResultado<T> = Repositorio.Resultado<T>
typealias RepoSucesso<T> = Repositorio.Resultado.Sucesso<T>
typealias RepoErro = Repositorio.Resultado.Erro

fun buscarDados(): RepoResultado<String> {
    return RepoSucesso("Dados carregados")
}

fun main() {
    when (val resultado = buscarDados()) {
        is RepoSucesso -> println(resultado.dados)
        is RepoErro -> println(resultado.mensagem)
        is Repositorio.Resultado.Carregando -> println("Carregando...")
    }
}

Type alias com tipos de plataforma

Quando voce integra Kotlin com bibliotecas Java ou APIs externas, os nomes de tipos podem ser longos e pouco descritivos:

import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ScheduledExecutorService

typealias CacheDeTokens = ConcurrentHashMap<String, Pair<String, Long>>
typealias Agendador = ScheduledExecutorService

class GerenciadorDeTokens(
    private val cache: CacheDeTokens = CacheDeTokens(),
    private val agendador: Agendador
) {
    fun armazenar(chave: String, token: String, expiracao: Long) {
        cache[chave] = token to expiracao
    }

    fun obter(chave: String): String? {
        val entrada = cache[chave] ?: return null
        return if (System.currentTimeMillis() < entrada.second) entrada.first else null
    }
}

Quando usar type alias

Type aliases sao mais uteis nos seguintes cenarios:

  • Tipos genericos profundamente aninhados como Map<String, List<Pair<Int, Double>>> que aparecem em varios pontos do codigo.
  • Assinaturas de funcao repetitivas quando um mesmo tipo de callback e usado em multiplas funcoes.
  • Nomes de dominio mais expressivos para tornar o codigo mais legivel no contexto do negocio, como typealias CPF = String ou typealias Reais = Double.
  • Encurtar referencias a classes internas de bibliotecas ou de seus proprios modulos.
  • Migracao gradual quando voce quer renomear um tipo sem alterar todo o codigo de uma vez.

Erros comuns

Confundir type alias com novo tipo

typealias CPF = String
typealias Email = String

fun enviarPara(email: Email) {
    println("Enviando para $email")
}

fun main() {
    val cpf: CPF = "123.456.789-00"
    enviarPara(cpf) // Compila sem erro! CPF e Email sao ambos String
}

O type alias nao cria um novo tipo. CPF e Email sao intercambiaveis porque ambos sao String. Se voce precisa de seguranca de tipos, use value classes (inline classes) em vez de type aliases.

Declarar dentro de funcoes ou classes

// ERRADO: type alias nao pode ser local
fun processar() {
    typealias Dados = List<String> // erro de compilacao
}

// CORRETO: declarar no nivel do arquivo
typealias Dados = List<String>
fun processar(dados: Dados) { /* ... */ }

Alias para tipos com variancia conflitante

open class Animal
class Cachorro : Animal()

typealias ListaAnimais = MutableList<Animal>

fun main() {
    val cachorros: MutableList<Cachorro> = mutableListOf(Cachorro())
    // val animais: ListaAnimais = cachorros // Erro: tipos incompativeis
    // MutableList<Cachorro> nao e subtipo de MutableList<Animal>
}

O alias nao altera as regras de variancia do tipo original. MutableList e invariante, entao MutableList<Cachorro> nao e subtipo de MutableList<Animal>, independentemente do alias.

Excesso de aliases

Criar aliases demais pode dificultar a navegacao no codigo. Se o tipo original ja e claro, o alias adiciona uma camada desnecessaria de indirecao. Use com moderacao.

Termos relacionados

  • Value class (inline class): cria um tipo wrapper com seguranca de tipos em tempo de compilacao, diferente do type alias que e apenas um apelido.
  • Generics: sistema de tipos parametrizados que frequentemente gera tipos longos beneficiados por aliases.
  • Funcao de ordem superior: funcoes que recebem ou retornam outras funcoes, cujas assinaturas sao frequentemente simplificadas com aliases.
  • Data class: tipo de classe em Kotlin para armazenar dados, frequentemente referenciada por aliases quando usada em colecoes.
  • Sealed class: hierarquia restrita de tipos que pode ter aliases para facilitar a referencia a subtipos.

Conclusao

Type aliases sao uma ferramenta simples mas eficaz para melhorar a legibilidade do codigo Kotlin. Eles nao criam novos tipos nem adicionam overhead em tempo de execucao. Use-os para simplificar tipos genericos complexos, assinaturas de funcao longas e referencias a classes profundamente aninhadas. Quando precisar de seguranca de tipos real, considere value classes como alternativa. O equiibrio entre legibilidade e clareza e a chave para usar type aliases de forma produtiva.