在您的示例中,T 代表一个类型。并且一旦设置该类型对于整个函数都是一致的。
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
如果在参数 a 的情况下 T 是一个 Int,那么在参数 b 的情况下它也必须是一个 Int。如该函数的使用所示:
var valueA = 2
var valueB = 4
swapTwoValues(&valueA, b: &valueB)
valueA // 4
valueB // 2
例如,我们不能将 String 替换为 Int,甚至不能将 Int 替换为 Double,但只要 Type 相同,则此泛型方法将采用任何 Type,因为它在所有其他方面都不受限制。
var valueA = "Hello"
var valueB = "Swift"
swapTwoValues(&valueA, b: &valueB)
valueA // "Swift"
valueB // "Hello"
但是,这并不意味着从泛型函数中排除了多种类型。您只需分配一个不同的字母来表示不同的类型(使用的字母无关紧要,只是使用 T 是因为它是 Type 的第一个字母,但没有理由不能用 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 方法的特征,这使得它们更容易被发现。