Los genéricos son una característica poderosa de Swift que te permite escribir funciones y tipos flexibles y reutilizables. Los genéricos pueden trabajar con cualquier tipo, sujeto a las restricciones que definas. En esta sección, aprenderás cómo funcionan los genéricos y cómo puedes utilizarlos para escribir código más robusto y reutilizable.

Conceptos Clave

  1. Definición de Genéricos: Los genéricos permiten que las funciones y los tipos trabajen con cualquier tipo de datos.
  2. Parámetros Genéricos: Los parámetros genéricos se definen utilizando un nombre de tipo genérico entre corchetes angulares (<T>).
  3. Restricciones de Tipo: Puedes restringir los tipos que se pueden usar con tus genéricos utilizando cláusulas where.

Ejemplo Básico de Función Genérica

Vamos a empezar con un ejemplo simple de una función genérica que intercambia dos valores:

func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

Explicación del Código

  • func swapTwoValues<T>(_ a: inout T, _ b: inout T): Aquí, T es un parámetro de tipo genérico. La función swapTwoValues puede trabajar con cualquier tipo T.
  • inout: Indica que los parámetros a y b pueden ser modificados dentro de la función.
  • let temporaryA = a: Guarda el valor de a en una variable temporal.
  • a = b: Asigna el valor de b a a.
  • b = temporaryA: Asigna el valor temporal (original de a) a b.

Uso de la Función Genérica

var int1 = 3
var int2 = 107
swapTwoValues(&int1, &int2)
print("int1: \(int1), int2: \(int2)") // int1: 107, int2: 3

var string1 = "hello"
var string2 = "world"
swapTwoValues(&string1, &string2)
print("string1: \(string1), string2: \(string2)") // string1: world, string2: hello

Tipos Genéricos

Además de funciones, también puedes definir tipos genéricos, como clases, estructuras y enumeraciones.

Ejemplo de Estructura Genérica

struct Stack<Element> {
    var items = [Element]()
    
    mutating func push(_ item: Element) {
        items.append(item)
    }
    
    mutating func pop() -> Element {
        return items.removeLast()
    }
}

Explicación del Código

  • struct Stack<Element>: Define una estructura genérica Stack que puede contener elementos de cualquier tipo Element.
  • var items = [Element](): Un array de elementos de tipo Element.
  • mutating func push(_ item: Element): Método para agregar un elemento al stack.
  • mutating func pop() -> Element: Método para eliminar y devolver el último elemento del stack.

Uso de la Estructura Genérica

var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
print(stackOfStrings.pop()) // tres

Restricciones de Tipo

Puedes restringir los tipos que se pueden usar con tus genéricos utilizando cláusulas where.

Ejemplo con Restricciones de Tipo

func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

Explicación del Código

  • func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int?: La función findIndex solo puede ser utilizada con tipos que conformen el protocolo Equatable.
  • if value == valueToFind: Compara los valores utilizando el operador ==, que está disponible porque T conforma Equatable.

Uso de la Función con Restricciones de Tipo

let arrayOfStrings = ["apple", "banana", "cherry"]
if let index = findIndex(of: "banana", in: arrayOfStrings) {
    print("El índice de banana es \(index)") // El índice de banana es 1
}

Ejercicios Prácticos

Ejercicio 1: Función Genérica de Intercambio

Escribe una función genérica que intercambie dos valores de cualquier tipo.

func swapValues<T>(_ a: inout T, _ b: inout T) {
    // Tu código aquí
}

Solución

func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

Ejercicio 2: Pila Genérica

Crea una estructura genérica Queue que funcione como una cola (FIFO).

struct Queue<Element> {
    // Tu código aquí
}

Solución

struct Queue<Element> {
    private var elements = [Element]()
    
    mutating func enqueue(_ element: Element) {
        elements.append(element)
    }
    
    mutating func dequeue() -> Element? {
        return elements.isEmpty ? nil : elements.removeFirst()
    }
}

Conclusión

En esta sección, has aprendido los conceptos básicos de los genéricos en Swift, cómo definir funciones y tipos genéricos, y cómo aplicar restricciones de tipo. Los genéricos son una herramienta poderosa que te permite escribir código más flexible y reutilizable. En el siguiente tema, exploraremos el manejo avanzado de errores en Swift.

© Copyright 2024. Todos los derechos reservados