Generics em Kotlin: para que servem e como usar: para iniciantes

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



Ao desenvolver em Kotlin, você frequentemente se depara com a necessidade de escrever código que possa funcionar com diferentes tipos de dados. Em vez de criar funções ou classes separadas para Int, String, Double e assim por diante, você pode usar o mecanismo Generics (genéricos). Este artigo explicará o que são Generics em Kotlin, para que servem e como aplicá-los na prática.



O que são Generics e para que servem?



Generics (genéricos) são uma ferramenta que permite escrever classes, interfaces e funções que funcionam com tipos de dados arbitrários. Imagine que você está criando uma caixa na qual pode colocar qualquer coisa: um livro, um telefone ou comida. Em vez de fazer uma caixa separada para cada item, você faz uma caixa universal.



Principais vantagens de usar Generics:

  • Type Safety (Segurança de Tipos) — o compilador verifica os tipos em tempo de compilação, prevenindo erros em tempo de execução.
  • Reutilização de código — o código escrito uma vez funciona com qualquer tipo.
  • Legibilidade — o código se torna mais compreensível, pois indica explicitamente com quais tipos ele trabalha.


Sem Generics, você teria que usar Any e fazer conversões de tipo constantemente, o que é propenso a erros. Com Generics, o compilador cuida da correção dos tipos.



Fundamentos de Generics em Kotlin: parâmetros de tipo



Em Kotlin, Generics são implementados através de parâmetros de tipo. Eles são especificados entre colchetes angulares <>. As letras mais usadas são T (Type), E (Element), K (Key), V (Value), mas você pode usar qualquer nome.



Funções genéricas



Vamos começar com um exemplo simples — uma função que retorna o argumento recebido:



fun <T> identity(value: T): T {    return value}

fun main() { val number: Int = identity(42) val text: String = identity("Olá") println(number) // 42 println(text) // Olá}


Aqui, <T> antes do nome da função declara o parâmetro de tipo. O tipo T é inferido automaticamente a partir do argumento, então você não precisa especificá-lo explicitamente.



Classes genéricas



As classes também podem ser genéricas. Vamos criar um contêiner simples:



class Box<T>(private val item: T) {    fun getItem(): T {        return item    }}

fun main() { val stringBox = Box("Livro") val intBox = Box(123)

println(stringBox.getItem()) // Livro println(intBox.getItem()) // 123}


A classe Box<T> pode armazenar um objeto de qualquer tipo. Ao criar uma instância, especificamos o tipo concreto, e o compilador garante que getItem() retornará exatamente esse tipo.



Restrições de tipo (Type Bounds)



Às vezes, é necessário restringir os tipos que podem ser usados em um Generic. Por exemplo, para que uma função funcione apenas com números. Para isso, usamos restrições (bounds) com a palavra-chave :.



Limite superior (Upper Bound)



fun <T : Number> sum(a: T, b: T): Double {    return a.toDouble() + b.toDouble()}

fun main() { println(sum(10, 20)) // 30.0 println(sum(3.5, 2.5)) // 6.0 // println(sum("A", "B")) // Erro de compilação}


Aqui, T : Number significa que o tipo T deve ser um subtipo da classe Number. Int, Double, Float são adequados, mas String não.



Múltiplas restrições



Se forem necessárias várias restrições, use where:



interface Printable {    fun print()}

fun <T>

Blogs

Book Recommendations