Generic (programación genérica) — es un mecanismo de Swift que permite escribir tipos y funciones con un tipo parametrizado
Puntos Clave
Generic (tipo genérico) — es una construcción sintáctica de Swift que permite escribir código flexible y reutilizable con parametrización de tipos. En lugar de un tipo concreto, se escribe un marcador de posición entre corchetes angulares — normalmente
Sin genéricos, habría que escribir funciones separadas para cada tipo: swapInts, swapStrings, swapDates. Una función genérica swap reemplaza las tres variantes con una sola declaración. Esto reduce la duplicación, mejora la legibilidad y disminuye la probabilidad de errores al copiar código.
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapValues(&x, &y)
El compilador de Swift genera código especializado para cada uso concreto de un genérico. Esto significa que la función genérica swapValues
Funciones genéricas son funciones que aceptan uno o más parámetros de tipo. Los parámetros de tipo se especifican después del nombre de la función entre corchetes angulares:
Una función puede tener múltiples parámetros genéricos. Por ejemplo, la función findKey
func findKey<K: Hashable, V>(
for value: V,
in dictionary: [K: V]
) -> K? where V: Equatable {
for (key, dictValue) in dictionary {
if dictValue == value {
return key
}
}
return nil
}
Las restricciones K: Hashable y where V: Equatable aseguran que findKey solo puede llamarse con un diccionario cuyas claves sean hashable y valores equatable. Tales restricciones no son burocracia sino una necesidad: sin Hashable no se puede indexar una búsqueda por clave, sin Equatable no se puede comparar value con dictValue.
where — es una palabra clave de Swift que impone restricciones adicionales a los parámetros genéricos. A diferencia de simplemente especificar un protocolo entre corchetes angulares (
La cláusula where se escribe después de la firma de la función o tipo, antes de la llave de apertura. En where se puede especificar que T: Comparable & Hashable (conforme a dos protocolos simultáneamente), o que T.U == Int (concretar un associated type).
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
}
extension Container where Item: Comparable {
func isSorted() -> Bool {
// Implementation available only if Item: Comparable
return true
}
}
La extensión con where añade métodos solo para aquellos tipos que cumplen la condición. Container
Associated Types (tipos asociados) — son una forma de hacer que un protocolo sea genérico sin especificar un tipo concreto en la etapa de declaración. En lugar de
Usando where se pueden establecer relaciones entre associated types de diferentes protocolos. Por ejemplo, se puede exigir que el Item de un protocolo coincida con Iterator.Element de otro. Esto garantiza la compatibilidad de tipos a nivel de compilación.
protocol StackProtocol {
associatedtype Element
mutating func push(_ item: Element)
mutating func pop() -> Element?
}
struct IntStack: StackProtocol {
typealias Element = Int
private var items: [Int] = []
mutating func push(_ item: Int) { items.append(item) }
mutating func pop() -> Int? { items.popLast() }
}
IntStack concreta Element como Int usando typealias. El compilador verifica que todos los requisitos del protocolo se cumplen con este tipo. Sin associated types, habría que escribir StackProtocol
Extensiones genéricas permiten añadir métodos a un tipo genérico con restricciones adicionales. Este es el patrón de “conformidad condicional”, donde un tipo obtiene funcionalidad solo bajo ciertas condiciones. Los subíndices también pueden ser genéricos.
Un subíndice puede ser genérico: acepta parámetros genéricos entre corchetes angulares. Esto es útil para el acceso seguro a colecciones con índices arbitrarios, donde el índice puede ser de diferentes tipos.
extension Array where Element: Numeric {
func sum() -> Element {
return reduce(0, +)
}
}
extension Array {
subscript<Indices: Sequence>(indices: Indices) -> [Element]
where Indices.Element == Int {
return indices.map { self[$0] }
}
}
Esta construcción proporciona una composición potente: el método sum aparece solo en arrays numéricos, y el subíndice con índices arbitrarios funciona con cualquier secuencia de índices enteros. El compilador gestiona la visibilidad de estos miembros basándose en las condiciones where.
Errores al usar genéricos a menudo están relacionados con restricciones incorrectas o el intento de usar genéricos donde se necesita un tipo concreto. Veamos tres escenarios comunes que enfrentan los desarrolladores.
Añadir parámetros genéricos donde un tipo concreto es suficiente es un antipatrón. Si una función siempre trabaja con String, no es necesario hacerla
Llamar a un método en un parámetro genérico sin especificar que el método existe es un error.
// ❌ Error: value has no count method
func countElements<T>>(value: T) -> Int { value.count }
// ✅ Works: T: Collection guarantees count exists
func countElements<T: Collection>(value: T) -> Int { value.count }
El error es claro: el genérico
Preguntas Frecuentes
Generic es una forma de escribir código que funciona con cualquier tipo, sin duplicar la misma lógica para Int, String y otros tipos. En lugar de un tipo concreto, se usa un marcador de posición
Generic preserva la información sobre el tipo concreto en tiempo de compilación, permitiendo al compilador verificar tipos y generar código optimizado. Any es borrado de tipo: cualquier valor puede pasarse, pero el tipo se pierde y se requiere conversión (as?).
Use la sintaxis T: SomeProtocol entre corchetes angulares o una cláusula separada where T: SomeProtocol. Las restricciones garantizan que el parámetro genérico soporta métodos y propiedades específicos.
Associated Type (associatedtype) es un marcador de posición para un tipo dentro de un protocolo. El protocolo no especifica un tipo concreto sino que declara un associatedtype, que se concreta en la implementación: Array tiene Element, Dictionary tiene Key y Value.
Swift compila el código genérico en versiones especializadas para cada tipo concreto. En la práctica, una función genérica para Int no es más lenta que una función escrita a mano para Int. No hay sobrecarga de abstracción.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también