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
并只编写一个函数,而不是为每种类型编写许多函数 Int
、Double
、Float
、String
等
如果不在您提供的示例中使用泛型,则必须为每个要交换的类型重载 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
。无法保证 a
和 b
是同一类型 - 例如,您不能交换 Int
和 String
。
在您的示例中,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 方法的特征,这使得它们更容易被发现。
我已经阅读了 Apple 开发者网站上的 Swift 教程,但我不了解泛型的概念。有谁能用简单的方式解释一下吗?例如:
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
本质上,这只是意味着它不是特定于类型的。您使用 T
并只编写一个函数,而不是为每种类型编写许多函数 Int
、Double
、Float
、String
等
如果不在您提供的示例中使用泛型,则必须为每个要交换的类型重载 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
。无法保证 a
和 b
是同一类型 - 例如,您不能交换 Int
和 String
。
在您的示例中,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 方法的特征,这使得它们更容易被发现。