Swift 2.0 中的泛型

Generics in Swift 2.0

我已经阅读了 Apple 开发者网站上的 Swift 教程,但我不了解泛型的概念。有谁能用简单的方式解释一下吗?例如:

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

本质上,这只是意味着它不是特定于类型的。您使用 T 并只编写一个函数,而不是为每种类型编写许多函数 IntDoubleFloatString

如果不在您提供的示例中使用泛型,则必须为每个要交换的类型重载 swapTwoValues。例如:

func swapTwoValues(inout a: Int, inout b: Int) {
    let temp = a
    a = b
    b = temp
}

func swapTwoValues(inout a: String, inout b: String) {
    let temp = a
    a = b
    b = temp
} 

// Many more swapTwoValues functions...

上述函数之间唯一不同的是它们接受的类型;每个里面的代码是完全一样的。因此,最好编写 one 可以接受任何类型的泛型函数。

请务必注意,您不能将 T 替换为 Any。无法保证 ab 是同一类型 - 例如,您不能交换 IntString

在您的示例中,T 代表类型。并且一旦设置该类型对于整个函数都是一致的。

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

如果 T 在参数 a 的情况下是 Int,那么在参数 b 的情况下它也必须是 Int。如函数的使用所示:

var valueA = 2
var valueB = 4
swapTwoValues(&valueA, b: &valueB)

valueA // 4
valueB // 2

例如,我们无法将 String 换成 Int,甚至无法将 Int 换成 Double,但只要类型相同,那么这个泛型方法将采用任何类型,因为它在所有其他方面都是不受限制的。

var valueA = "Hello"
var valueB = "Swift"
swapTwoValues(&valueA, b: &valueB)

valueA // "Swift"
valueB // "Hello"

然而,这并不意味着多种类型被排除在泛型函数之外。您只需要分配一个不同的字母来表示不同的类型(使用的字母无关紧要,只是使用 T 是因为它是类型的第一个字母,但没有理由不能用 Q 代替,例如,或任何其他字母):

func swapTwoValues<T,S>(inout a: T, inout b: T, inout c: S, inout d: S) {
    let temporaryA = a
    a = b
    b = temporaryA

    let temporaryC = c
    c = d
    d = temporaryC
}

var valueA = 2
var valueB = 4

var valueC = "Hello"
var valueD = "Swift"
swapTwoValues(&valueA, b: &valueB, c:&valueC, d:&valueD)

valueA  // 4
valueB // 2

valueC // "Swift"
valueD // "Hello"

注意:我们仍然不能将 T 换成 S,因为 Swift 是一种强类型语言,我们无法保证它们是相同的。

当涉及协议来约束泛型类型时,它变得更加有趣。在这里,我使用 UnsignedIntegerType:

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

var valueA:UInt = 10
var valueB:UInt = 11

swapTwoValues(&valueA, b: &valueB)

现在只有 UInt、UInt8、UInt32 等类型是可接受的,所有其他值都将被拒绝并产生错误。

注意:之所以使用协议来约束类型,是为了保证某些方法能够正常工作。例如,如果需要一个泛型函数来创建一个新的类型实例,那么它必须采用一个带有 init 方法的协议。 (您可以在 Apple 的文档中查看每种类型的协议采用情况。)

我们可以更进一步,使用 where 关键字来确定泛型集合中包含的类型:

func swapTwoValues<T: CollectionType where T.Generator.Element: UnsignedIntegerType>(inout a: T, inout b: T) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var valueA:[UInt] = [10,12,4]
var valueB:[UInt] = [11,45,67]

swapTwoValues(&valueA, b: &valueB)

valueA  // [11, 45, 67]
valueB // [10, 12, 4]

或者使用 ==:

检查第二种类型是否等同于集合中元素的类型
func swapTwoValues<T: CollectionType, S where S == T.Generator.Element>(inout a: T, inout b: T, inout c: S, inout d: S) {
    let temporaryA = a
    a = b
    b = temporaryA

    let temporaryC = c
    c =  d
    d = temporaryC
}

进一步阅读:protocol extensions in Swift 2 变得更加有趣,因为现在泛型函数可以具有 Type 方法的特征,这使得它们更容易被发现。