Swift Genéricos
Swift proporciona genéricos para que puedas escribir funciones y tipos flexibles y reutilizables.
La biblioteca estándar de Swift está construida con código genérico.
Los tipos Array y Dictionary de Swift son colecciones genéricas.
Puedes crear un array de Int, o un array de String, o incluso un array de datos de cualquier otro tipo de Swift.
El siguiente ejemplo es una función no genérica exchange que se usa para intercambiar dos valores Int:
Ejemplo
El resultado de la ejecución del programa anterior es:
交换前数据: 100 和 200 交换后数据: 200 和 100
El ejemplo anterior solo sirve para intercambiar variables de tipo entero Int. Si quieres intercambiar dos valores String o Double, tienes que reescribir una función correspondiente, por ejemplo swapTwoStrings(_:_:) y swapTwoDoubles(_:_:), como se muestra a continuación:
Función de intercambio de valores String y Double
A partir del código anterior, el código funcional es el mismo, solo difiere en el tipo. En este caso podemos usar genéricos para evitar escribir código repetido.
Los genéricos utilizan un nombre de tipo placeholder (representado aquí por la letra T) en lugar del nombre de tipo real (por ejemplo, Int, String o Double).
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
swapTwoValues va seguido del nombre de tipo placeholder (T), encerrado entre corchetes angulares (<T>). Estos corchetes angulares le indican a Swift que T es un nombre de tipo placeholder dentro de la definición de la función swapTwoValues(_:_:), por lo tanto Swift no buscará un tipo real llamado T.
El siguiente ejemplo es una función genérica exchange que se usa para intercambiar dos valores Int y String:
Ejemplo
El resultado de la ejecución del programa anterior es:
交换前数据: 100 和 200 交换后数据: 200 和 100 交换前数据: A 和 B 交换后数据: B 和 A
Tipos genéricos
Swift te permite definir tus propios tipos genéricos.
Las clases, estructuras y enumeraciones personalizadas pueden trabajar con cualquier tipo, al igual que Array y Dictionary.
A continuación, escribiremos un tipo de colección genérico llamado Stack (pila), que solo permite agregar nuevos elementos al final de la colección (llamado apilar) y también solo permite eliminar elementos desde el final (llamado desapilar).

La imagen se explica de izquierda a derecha de la siguiente manera:
- Hay tres valores en la pila.
- Un cuarto valor se empuja a la cima de la pila.
- Ahora hay cuatro valores en la pila, el que se apiló más recientemente está en la cima.
- El valor más alto de la pila se elimina, o se denomina desapilar.
- Después de eliminar un valor, ahora la pila vuelve a tener solo tres valores.
El siguiente ejemplo es una versión no genérica de la pila, tomando como ejemplo una pila de tipo Int:
Pila de tipo Int
Esta estructura utiliza una propiedad Array llamada items para almacenar valores en la pila. Stack proporciona dos métodos: push(_:) y pop(), que se usan para insertar valores en la pila y eliminarlos de la pila. Estos métodos están marcados como mutating porque necesitan modificar el array items de la estructura.
La estructura IntStack anterior solo se puede usar con el tipo Int. Sin embargo, se puede definir una estructura Stack genérica para poder manejar valores de cualquier tipo.
A continuación se muestra la versión genérica del mismo código:
Pila genérica
El resultado de la ejecución del ejemplo es:
字符串元素入栈: ["google", "ejemplo"] 出栈元素: ejemplo 整数元素入栈: [1, 2]
Stack es básicamente igual a IntStack, con el parámetro de tipo placeholder Element reemplazando al tipo real Int.
En el ejemplo anterior, Element se usa como marcador de posición en los siguientes tres lugares:
- Crearitemsla propiedad, usandoElementun array vacío de tipo Element para inicializarla.
- Especificarpush(_:)el único parámetro del métodoitemdebe ser de tipoElementtipo.
- Especificarpop()el tipo de retorno del método debe serElementtipo.
Extensión de tipos genéricos
Cuando extiendes un tipo genérico (usando la palabra clave extension), no necesitas proporcionar una lista de parámetros de tipo en la definición de la extensión. Más convenientemente, la lista de parámetros de tipo declarada en la definición del tipo original está disponible en la extensión, y estos nombres de parámetros del tipo original se utilizan como referencias a los parámetros de tipo de la definición original.
El siguiente ejemplo extiende el tipo genérico Stack y le agrega una propiedad computada de solo lectura llamada topItem, que devolverá el elemento en la cima actual de la pila sin eliminarlo de la pila:Genéricos
En el ejemplo, la propiedad topItem devuelve un valor opcional de tipo Element. Cuando la pila está vacía, topItem devuelve nil; cuando la pila no está vacía, topItem devuelve el último elemento del array items.
El resultado de la ejecución del programa anterior es:
字符串元素入栈: 栈中的顶部元素是:ejemplo. ["google", "ejemplo"]
También podemos especificar un tipo asociado extendiendo un tipo existente.
Por ejemplo, el tipo Array de Swift ya proporciona el método append(_:), una propiedad count y un subíndice que acepta un valor de índice de tipo Int para acceder a sus elementos. Estas tres funcionalidades cumplen con los requisitos del protocolo Container, por lo que solo necesitas declarar que Array adopta ese protocolo para extender Array.
El siguiente ejemplo simplemente crea una extensión vacía:
extension Array: Container {}
Restricciones de tipo
Una restricción de tipo especifica que un parámetro de tipo debe heredar de una clase específica, o cumplir con un protocolo específico o una composición de protocolos.
Sintaxis de restricción de tipo
Puedes escribir una restricción de tipo después del nombre de un parámetro de tipo, separada por dos puntos, como parte de la cadena de parámetros de tipo. La sintaxis básica de esta restricción de tipo aplicada a funciones genéricas es la siguiente (igual que la sintaxis para tipos genéricos):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 这里是泛型函数的函数体部分
}
Esta función tiene dos parámetros de tipo. El primer parámetro de tipo, T, tiene una restricción de tipo que requiere que T sea una subclase de SomeClass; el segundo parámetro de tipo, U, tiene una restricción de tipo que requiere que U cumpla con el protocolo SomeProtocol.
Ejemplo
Genéricos
El índice comienza desde 0.
El resultado de la ejecución del programa anterior es:
ejemplo 的索引为 3
Tipos asociados
En Swift se usa la palabra clave associatedtype para definir un tipo asociado.
El siguiente ejemplo define un protocolo Container, que define un tipo asociado ItemType.
El protocolo Container solo especifica tres funcionalidades que cualquier tipo que cumpla con el protocolo Container debe proporcionar. Los tipos que cumplen con el protocolo también pueden proporcionar otras funcionalidades adicionales si satisfacen estas tres condiciones.
// Container 协议
protocol Container {
associatedtype ItemType
// 添加一个新元素到容器里
mutating func append(_ item: ItemType)
// 获取容器中元素的数
var count: Int { get }
// 通过索引值类型为 Int 的下标检索到容器中的每一个元素
subscript(i: Int) -> ItemType { get }
}
// Stack 结构体遵从 Container 协议
struct Stack<Element>: Container {
// Stack<Element> 的原始实现部分
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container 协议的实现部分
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
var tos = Stack<String>()
tos.push("google")
tos.push("ejemplo")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素个数
print( tos.count)
El resultado de la ejecución del programa anterior es:
["google", "ejemplo", "taobao"] 3
Sentencia where
Las restricciones de tipo pueden asegurar que un tipo cumpla con las restricciones definidas por una función o clase genérica.
Puedes definir restricciones para los parámetros en la lista de parámetros mediante una sentencia where.
Puedes escribir una sentencia where inmediatamente después de la lista de parámetros de tipo. La sentencia where va seguida de una o más restricciones sobre los tipos asociados, y/o una o más relaciones de igualdad entre tipos y tipos asociados.
Ejemplo
El siguiente ejemplo define una función genérica llamada allItemsMatch, que se usa para verificar si dos instancias de Container contienen los mismos elementos en el mismo orden.
Si todos los elementos coinciden, devuelve true; de lo contrario, devuelve false.
Genéricos
El resultado de ejecución del programa anterior es:
Coincidir con todos los elementosOtras extensiones