Lição: 26: Generics: Introdução e Uso
Generics no Swift são um recurso poderoso que permite escrever código flexível e reutilizável. Em vez de criar várias versões de uma função ou tipo para diferentes tipos de dados, você pode usar generics para operar em qualquer tipo que satisfaça certos requisitos. Essa capacidade permite que você crie um código mais abstrato e menos vinculado a tipos de dados específicos.
O que são Generics?
Generics permitem definir funções, tipos e estruturas de dados que podem trabalhar com qualquer tipo de dado. Ao usar generics, você define um espaço reservado para o tipo de dado que pode ser substituído por um tipo específico mais tarde.
Funções Genéricas
Uma função genérica é uma função que pode trabalhar com qualquer tipo de dado. Você define uma função genérica adicionando um parâmetro de tipo entre sinais de menor e maior (<T>
) antes da lista de parâmetros da função.
Aqui está um exemplo de uma função genérica simples que troca dois valores:
func trocarValores<T>(a: inout T, b: inout T) {
let temp = a
a = b
b = temp
}
var x = 5
var y = 10
trocarValores(&x, &y)
print("x: \(x), y: \(y)") // x: 10, y: 5
var str1 = "Olá"
var str2 = "Mundo"
trocarValores(&str1, &str2)
print("str1: \(str1), str2: \(str2)") // str1: Mundo, str2: Olá
No exemplo acima, a função trocarValores
aceita dois parâmetros de qualquer tipo T
e troca seus valores.
Tipos Genéricos
Você também pode definir seus próprios tipos genéricos, como classes, estruturas e enums. Isso permite que você crie estruturas de dados que podem lidar com diferentes tipos. Aqui está um exemplo de uma pilha genérica:
struct Pilha<T> {
private var itens: [T] = []
mutating func empilhar(_ item: T) {
itens.append(item)
}
mutating func desempilhar() -> T? {
return itens.isEmpty ? nil : itens.removeLast()
}
func topo() -> T? {
return itens.last
}
var vazia: Bool {
return itens.isEmpty
}
var contagem: Int {
return itens.count
}
}
var pilhaInt = Pilha<Int>()
pilhaInt.empilhar(1)
pilhaInt.empilhar(2)
print(pilhaInt.desempilhar()!) // 2
print(pilhaInt.topo()!) // 1
var pilhaString = Pilha<String>()
pilhaString.empilhar("A")
pilhaString.empilhar("B")
print(pilhaString.desempilhar()!) // B
print(pilhaString.contagem) // 1
Neste exemplo, criamos uma estrutura Pilha
que pode armazenar elementos de qualquer tipo T
. Em seguida, demonstramos o uso da Pilha
com os tipos Int
e String
.
Restrições em Generics
Às vezes, pode ser necessário restringir os tipos que podem ser usados com uma função ou tipo genérico. Você pode fazer isso usando restrições de tipo. Por exemplo, se você deseja uma função genérica que somente funcione com tipos que implementam o protocolo Comparable
, pode fazê-lo assim:
func encontrarMax<T: Comparable>(a: T, b: T) -> T {
return a > b ? a : b
}
let maxInt = encontrarMax(a: 10, b: 20)
print("Máximo Int: \(maxInt)") // Máximo Int: 20
let maxString = encontrarMax(a: "Maçã", b: "Banana")
print("Máximo String: \(maxString)") // Máximo String: Banana
Neste exemplo, a função encontrarMax
pode ser usada apenas com tipos de dados que implementam o protocolo Comparable
, ou seja, que podem ser comparados usando o operador >
.
Conclusão
Generics são um recurso poderoso no Swift que permite escrever código flexível e reutilizável. Ao usar funções e tipos genéricos, você pode criar abstrações que podem trabalhar com qualquer tipo de dado, tornando seu código mais manutenível e escalável. À medida que você se familiariza mais com generics, descobrirá que eles podem melhorar significativamente sua capacidade de escrever um código Swift limpo e eficiente.