The Swift Programming Language学习笔记(二十四)——泛型
泛型泛型代码可以让你编写适用自定义需求以及任意类型的灵活可重用的函数和类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。
泛型所解决的问题func swapTwoInts(inout a: Int,inout _ b: Int) {
let temp = a
a = b
b = temp
}
var x = 10
var y = 20
swapTwoInts(&x,&y)
print(x)
print(y)
func swapTwoDoubles(inout a: Double,inout _ b: Double) {
let temp = a
a = b
b = temp
}
var m = 1.2
var n = 2.3
swapTwoDoubles(&m,&n)
print(m)
print(n)
func swapTwoStrings(inout a: String,inout _ b: String) {
let temp = a
a = b
b = temp
}
var p = "aaaa"
var q = "bbbb"
swapTwoStrings(&p,&q)
print(p)
print(q)
在上面三个函数中, 泛型函数泛型函数可以适用于任何类型。 func swapTwoValues<T>(inout a: T,inout _ b: T) {
let tempA = a
a = b
b = tempA
}
var x = 1
var y = 2
swapTwoValues(&x,&y)
print(x)
print(y)
var m = "Hello"
var n = "World"
swapTwoValues(&m,&n)
print(m)
print(n)
swap(&m,&n) // 调用Swift标准库中的泛型函数
print(m)
print(n)
这个函数的泛型版本使用了 另外一个不同之处在于这个泛型函数名后面跟着占位类型名( 上面定义的 类型参数一旦一个类型参数被指定,你可以用它来定义一个函数的参数类型(例如 命名类型参数在大多数情况下,类型参数具有一个描述性名字,例如 注意,请始终使用大写字母开头的驼峰式命名法(例如 泛型类型除了泛型函数,Swift还允许你定义 栈是一系列值的有序集合,和 注意,栈的概念已被 struct IntStack {
var items = [Int]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
}
struct Stack<Element> {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var s = Stack<String>()
s.push("abc")
s.push("def")
s.push("ghi")
s.push("jkl")
let a = s.pop()
print(a)
扩展一个泛型类型当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。 struct Stack<Element> {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
/** * 这个扩展并没有定义一个类型参数列表。相反的,Stack类型已有的类型参数名称Element,被用在扩展中来表示计算型属性topItem的可选类型。 */
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
var s = Stack<String>()
s.push("abc")
s.push("def")
s.push("ghi")
s.push("jkl")
if let item = s.topItem { // 可选绑定
print(item)
}
类型约束有的时候如果能将使用在 例如,Swift的 这个要求强制加上了一个类型约束作用于 当你创建自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。抽象概念,例如可哈希的,描述的是类型在概念上的特征,而不是它们的显式类型。 类型约束语法可以在一个类型参数名后面放置一个类名或者协议名,通过冒号分隔,从而定义类型约束,它们将作为类型参数列表的一部分。这种基本的类型约束作用于泛型函数时的语法如下所示(作用于泛型类型时的语法与之相同)。 func someFunction<T: SomeClass,U: SomeProtocol>(someT: T,someU: U) {
// 这里是泛型函数的函数体部分
}
类型约束实践func findStringIndex(array: [String],_ valueToFind: String) -> Int? {
for (index,value) in array.enumerate() {
if value == valueToFind {
return index
}
}
return nil
}
let s = ["a","b","c"]
if let i = findStringIndex(s,"b") {
print("index = (i)")
} else {
print("not found")
}
func findIndex<T: Equatable>(array: [T],_ valueToFind: T) -> Int? {
for (index,value) in array.enumerate() {
if value == valueToFind { // 不加: Equatable会报错:error: binary operator '==' cannot be applied to two 'T' operands
return index
}
}
return nil
}
print(findIndex([1.1,2.2,3.3],4.4)) // nil
print(findIndex(["11","22","333"],"333")!) // 2
在上面的代码中,不加 不是所有的Swift类型都可以用等式符( 不过,所有的这些并不会让我们无从下手。Swift标准库中定义了一个 加了 关联类型定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型作为协议的一部分,为某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 关联类型实践protocol Container {
typealias ItemType // Container协议需要在不知道容器中元素的具体类型的情况下引用这种类型,因此声明了一个关联类型ItemType,这个协议无法定义ItemType是什么类型的别名,这个信息将留给采纳协议的类型来提供。
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
struct IntStack2: Container {
var items = [Int]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// 遵循的Container协议的实现部分
typealias ItemType = Int // IntStack指定ItemType为Int类型,将Container协议中抽象的ItemType类型转换为具体的Int类型。
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
/** * 由于Swift的类型推断,你实际上不用在IntStack的定义中声明ItemType为Int。因为IntStack符合Container协议的所有要求,Swift 只需通过append(_:)方法的item参数类型和下标返回值的类型,就可以推断出ItemType的具体类型。事实上,如果你在上面的代码中删除了typealias ItemType = Int这一行,这一切仍旧可以正常工作,因为Swift清楚地知道ItemType应该是何种类型。 */
struct IntStack: Container {
var items = [Int]()
mutating func push(item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// 遵循的Container协议的实现部分
// typealias ItemType = Int // IntStack指定ItemType为Int类型,将Container协议中抽象的ItemType类型转换为具体的Int类型。
mutating func append(item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
/** * 泛型版本的遵循了Container协议的Stack。 * 占位类型参数Element被用作append(_:)方法的item参数和下标的返回类型。Swift可以据此推断出Element的类型即是ItemType的类型。 */
struct Stack<Element>: Container {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func append(item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i:Int) -> Element {
return items[i]
}
}
由于Swift的类型推断,你实际上不用在 而在泛型类型 通过扩展一个存在的类型来指定关联类型extension Array: Container {}
Swift的
where子句为关联类型定义约束也是非常有用的。你可以在参数列表中通过 protocol Container {
typealias ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
struct Stack<Element>: Container {
var items = [Element]()
mutating func push(item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func append(item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i:Int) -> Element {
return items[i]
}
}
extension Array: Container {}
/**
* 被检查的两个Container可以不是相同类型的容器(虽然它们可以相同),但它们必须拥有相同类型的元素。这个要求通过一个类型约束以及一个where子句来表示
*/
func allItemsMatch<C1: Container,C2: Container where C1.ItemType == C2.ItemType,C1.ItemType: Equatable>(aContainer: C1,_ bContainer: C2) -> Bool {
if aContainer.count != bContainer.count {
return false
}
for i in 0..<aContainer.count {
if aContainer[i] != bContainer[i] {
return false
}
}
return true
}
let arr = ["abc","def","ghi"]
var s = Stack<String>()
s.append("abc")
s.append("def")
s.append("ghi")
print(allItemsMatch(arr,s)) // true
即使栈和数组是不同的类型,但它们都符合 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |