O que são Generics em Kotlin?

Generics permitem criar classes, interfaces e funções que funcionam com qualquer tipo, mantendo a segurança de tipos em tempo de compilação. Em vez de escrever código específico para Int, String ou qualquer outro tipo, você escreve uma vez e reutiliza com qualquer um deles.

Classe genérica

class Caixa<T>(val conteudo: T) {
    fun abrir(): T {
        println("Abrindo a caixa...")
        return conteudo
    }
}

fun main() {
    val caixaDeTexto = Caixa("Presente surpresa")
    val caixaDeNumero = Caixa(42)

    println(caixaDeTexto.abrir()) // Presente surpresa
    println(caixaDeNumero.abrir()) // 42
}

O T é um parâmetro de tipo — pode ser qualquer coisa. O compilador garante que você não misture os tipos.

Função genérica

fun <T> trocar(par: Pair<T, T>): Pair<T, T> {
    return Pair(par.second, par.first)
}

fun main() {
    val original = Pair("Kotlin", "Java")
    val trocado = trocar(original)
    println(trocado) // (Java, Kotlin)
}

Restrições de tipo

Você pode restringir quais tipos são aceitos usando where ou ::

fun <T : Comparable<T>> maior(a: T, b: T): T {
    return if (a > b) a else b
}

fun main() {
    println(maior(10, 20))         // 20
    println(maior("Kotlin", "Java")) // Kotlin
}

Variância: in e out

Kotlin tem um sistema de variância mais explícito que Java:

// out = produtor (covariant) — só retorna T
interface Fonte<out T> {
    fun obter(): T
}

// in = consumidor (contravariant) — só recebe T
interface Destino<in T> {
    fun enviar(item: T)
}

class FonteDeString : Fonte<String> {
    override fun obter() = "Kotlin Brasil"
}

fun main() {
    val fonte: Fonte<Any> = FonteDeString() // Funciona por causa do out
    println(fonte.obter())
}
  • out T significa que T só aparece como retorno (posição de saída)
  • in T significa que T só aparece como parâmetro (posição de entrada)

Projeção de tipo (star projection)

Quando você não se importa com o tipo genérico, use *:

fun imprimirItens(lista: List<*>) {
    lista.forEach { println(it) }
}

Generics são essenciais para escrever código reutilizável e type-safe. A biblioteca padrão do Kotlin usa generics em praticamente tudo: listas, maps, flows e muito mais.