📜  Swift-泛型(1)

📅  最后修改于: 2023-12-03 15:20:25.318000             🧑  作者: Mango

Swift-泛型

Swift是一种类型安全的语言,这意味着编译器可以帮助我们在编译时捕捉到许多类型错误。但是对于我们编写的某些代码,我们可能不确定将要处理的值的确切类型。这正是泛型的用处所在。

什么是泛型?

泛型就是让代码可以处理任何类型的数据,而不需要重复编写多个相同的函数或类。泛型代码可以大大提高代码的可重用性、性能和灵活性。

泛型函数

在Swift中,我们可以使用泛型函数来处理任意类型的数据。泛型函数使用占位符类型来代替实际类型。这些占位符类型可以被用在函数体内作为参数类型、返回类型或者函数体内的临时变量。下面是一个例子:

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

在这个例子中,我们使用了<T>来定义泛型占位符。在函数参数列表中,_ a: inout T_ b: inout T 都表示这两个参数应该是同一个未知类型T。函数体中的临时变量temporaryA是类型为T的值。函数用来交换a和b的值。

我们可以使用不同类型的变量调用这个函数,比如Int和String:

var a = 10
var b = 20

swapTwoValues(&a, &b)
print("a is now \(a), and b is now \(b)") // output: a is now 20, and b is now 10

var str1 = "hello"
var str2 = "world"

swapTwoValues(&str1, &str2)
print("str1 is now \(str1), and str2 is now \(str2)") // output: str1 is now world, and str2 is now hello
泛型类型

除了泛型函数,我们还可以定义泛型类型。泛型类型可以是类、结构体或枚举。下面是一个泛型Stack类的例子:

struct Stack<T> {
    var items = [T]() // 泛型类型T的数组

    mutating func push(_ item: T) {
        items.append(item)
    }

    mutating func pop() -> T {
        return items.removeLast()
    }

    func peek() -> T {
        return items[items.count - 1]
    }

    var isEmpty: Bool {
        return items.isEmpty
    }
}

Stack结构体有一个泛型类型T,用于存储items数组中的元素类型。结构体还有一个push方法,用于将元素添加到栈顶,并有一个pop方法用于从栈顶弹出一个元素。peek方法返回栈顶元素,而isEmpty属性用于检查栈是否为空。

我们可以将不同类型的值添加到Stack中:

var myStack = Stack<Int>()
myStack.push(10)
myStack.push(20)
myStack.push(30)

print(myStack.peek()) // output: 30

var anotherStack = Stack<String>()
anotherStack.push("hello")
anotherStack.push("world")

print(anotherStack.peek()) // output: world
关联类型

有时候我们需要在协议中声明一个或多个类型,但是我们并不知道具体的类型是什么。这时候就可以用关联类型来解决这个问题。

protocol Container {
    associatedtype ItemType
    mutating func append(_ item: ItemType)
    var count: Int { get }
    subscript(i: Int) -> ItemType { get }
}

在Container协议中,我们定义了一个关联类型ItemType。该类型在协议中使用,但是我们不知道ItemType的具体类型。实现该协议的类型需要提供ItemType的实际类型。

下面是一个实现了Container协议的IntStack结构体:

struct IntStack: Container {
    // IntStack的原始实现部分
    var items = [Int]()

    mutating func push(_ item: Int) {
        items.append(item)
    }

    mutating func pop() -> Int {
        return items.removeLast()
    }

    // Container协议的实现部分
    typealias ItemType = Int // 实际上,这是默认的,你可以直接忽略这个代码

    mutating func append(_ item: Int) {
        self.push(item)
    }

    var count: Int {
        return items.count
    }

    subscript(i: Int) -> Int {
        return items[i]
    }
}

这个结构体实现了Int类型的Stack,并通过遵循Container协议来进行扩展。我们还重新定义了Container协议的关联类型ItemType,这里使用了默认类型Int。同样,typealias ItemType = Int可以直接省略。由于遵循Container协议,我们需要提供必要的方法,如append、count和下标语法。

泛型扩展

在Swift中,我们也可以对泛型类型进行扩展。当我们需要对已有类型进行更多泛型功能扩展时,这个功能就非常实用。我们可以使用泛型约束来限制扩展只能用于特定类型。下面是一个扩展Array的示例,它提供了排序方法:

extension Array where Element: Comparable {
    func sortBySelection() -> [Element] {
        var result = self
        for i in 0 ..< result.count - 1 {
            var minIndex = i
            for j in i + 1 ..< result.count {
                if result[j] < result[minIndex] {
                    minIndex = j
                }
            }
            if i != minIndex {
                result.swapAt(i, minIndex)
            }
        }
        return result
    }
}

在这个扩展中,我们使用Element这个泛型类型代表了数组中的元素类型。使用where Element: Comparable的语法来限制扩展只适用于实现了Comparable协议的元素类型。

总结

泛型是Swift中一个非常强大的特性,它可以让我们更加灵活地编写通用代码,并提高代码的重用性。泛型函数、泛型类型、关联类型和泛型扩展都是泛型在Swift中的常见用法。如果你想编写优美的、易于扩展和重用的代码,泛型是不容错过的。