Generics em Kotlin: para que servem e como usar na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Generics em Kotlin: para que servem e como usar na prática



Generics (genéricos) — um dos recursos-chave da linguagem Kotlin, que permite escrever código mais flexível, type-safe e reutilizável. Se você já escreveu List<String> ou Map<Int, User>, já encontrou genéricos. Mas para que eles realmente servem e como extrair o máximo deles? Neste artigo, vamos analisar a teoria e passar para a prática com exemplos reais.



O problema que os Generics resolvem



Imagine que você precisa escrever uma função que retorna o primeiro elemento de uma lista. Sem genéricos, você teria que escrever uma função separada para cada tipo de dado:



fun getFirstString(list: List<String>): String? = list.firstOrNull()fun getFirstInt(list: List<Int>): Int? = list.firstOrNull()fun getFirstUser(list: List<User>): User? = list.firstOrNull()


Isso leva à duplicação de código. Usar Any resolveria o problema, mas você perderia a informação do tipo em tempo de compilação:



fun getFirst(list: List<Any>): Any? = list.firstOrNull()

// Seria necessário fazer casting de tiposval name: String? = getFirst(strings) as? String


Os Generics resolvem esse problema de forma elegante:



fun <T> getFirst(list: List<T>): T? = list.firstOrNull()

// O compilador sabe o tipo!val name: String? = getFirst(listOf("Alice", "Bob"))val age: Int? = getFirst(listOf(25, 30))


Agora uma função funciona com qualquer tipo, mantendo total type-safety.



Fundamentos dos Generics em Kotlin: sintaxe e declaração



Genéricos são declarados usando colchetes angulares <T>. A letra T é apenas uma convenção; você pode usar qualquer nome: E (elemento), K (chave), V (valor).



Classes Genéricas



class Box<T>(val value: T) {    fun getContent(): T = value}

fun main() { val stringBox = Box("Hello") val intBox = Box(42) println(stringBox.getContent()) // Hello println(intBox.getContent()) // 42}


Funções Genéricas



fun <T> printItem(item: T) {    println("Item: $item")}

// Pode-se especificar o tipo explicitamenteprintItem<String>("Kotlin")// Ou confiar na inferência de tiposprintItem(100)


Interfaces Genéricas



interface Repository<T> {    fun getById(id: Int): T    fun save(item: T): Boolean}

class UserRepository : Repository<User> { override fun getById(id: Int): User { /* ... */ } override fun save(item: User): Boolean { /* ... */ }}


Variância: in, out e asterisco *



Um dos recursos mais poderosos do Kotlin é a variância (variance). Ela responde à pergunta: pode List<String> ser um subtipo de List<Any>?



Covariância (out)



Use out quando seu tipo genérico é apenas retornado (producer). Por exemplo, List<T> é covariante:



// Você pode atribuir List<String> a uma variável List<Any>val strings: List<String> = listOf("a", "b")val anyList: List<Any> = strings // OK!

// Mas não pode adicionar elementos// anyList.add(1) // Erro de compilação!


Exemplo próprio:



class Producer<out T>(private val value: T) {    fun produce(): T = value}

fun main() { val stringProducer: Producer<String> = Producer("Hello") val anyProducer: Producer<Any> = stringProducer // Funciona! println(anyProducer.produce())}


Contravariância (in)



Use in quando o tipo é apenas recebido (consumer). Por exemplo, Comparable<T>:





                
                

Blogs

Book Recommendations