// 泛型 代码能够让你根据自定义的需求,编写出适用于任意类型, 灵活可重用的函数以及类型, 它能让你避免代码的重复, 用一种清晰和抽象的方式来表达代码的意图

// 泛型是 Swift 最强大的特性之一, 许多 Swift 标准库是通过泛型代码构建的,事实上, 泛型的使用贯穿了整本语言手册, 只是你可能没有发现而已, 例如, Swift 的 Array 和 Dictionary 都是泛型集合, 你可以创建一个 Int 数组, 也可以创建一个 String 数组, 甚至可以是任意其他 Swift 类型的数组, 同样的, 你也可以创建存储任意指定类型的字典

// 泛型所解决的问题

// 下面是一个 标准的非泛型函数 swapTwoInts(_:_:), 用来交换两个 Int 值:

func swapTwoInts(_ a: inout Int, _ b: inout Int){

let temporaryA = a

a = b

b = temporaryA

}

// 这个函数使用输入输出参数(inout) 来交换 a 和 b 的值,

var someInt = 3

var anotherInt = 107

swap(&someInt, &anotherInt)

print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")

// 诚然,swapTwoInts(_:_:) 函数挺有用, 但是他只能交换 Int 值, 如果你想要交换两个 String 值 或者 Double 值, 就不得不写更多的函数

// 在实际应用中, 通常需要一个更实用更灵活的函数来交换两个任意类型的值, 泛型代码帮你解决了这两种问题,

// 泛型函数

// 泛型函数可以适用于任何类型, 下面的 swapTwoValue(_:_:) 函数是上面的泛型版本

func swapTwoValues<T>(_ a: inout T, _ b: inout T){

let temporaryA = a

a = b

b = temporaryA

}

// 这个函数的泛型版本使用了占位类型名 (在这里用字母 T 表示) 来代替实际类型名 (例如 Int, Sreing , Double) 占位类型名没有指明 T 必须是什么类型, 单是他指明了 a 和 b 必须是同一类型 T ,无论 T 代表什么类型, 只有 swapTwoValues(_:_:) 函数在调用时,才能根据传入的实际类型决定 T 所代表的类型

// 另外一个不同之处在于这个泛型函数名 (swapTwoValues(_:_:)) 后面跟着的占位类型名 (T), 并用尖括号括起来 ( <T> ), 这个尖括号告诉 Swift 你那个 (T) 是 swapTwoValues(_:_:) 函数定义内的一个占位类型名, 因此 Swift 不会去检查名为 (T) 的实际类型

// swapTwoValues(_:_:) 函数现在可以像 swapTwoInts(_:_:) 那样调用, 不同的是它能接受两个任意类型的值, 条件是这两个值有相同的类型, swapTwoValues(_:_:) 函数被调用时, T 所代表的类型都会由传入的值的类型推断出来

// 注意: 上面定义的 swapTwoValues(_:_:) 函数 是受 swap(_:_:) 函数启发而发现的, 后者存在于 Swift 标准库,你可以在你的应用程序中使用它

// 类型参数

// 在上面的 swapTwoValues(_:_:) 例子中,占位类型 T 是 类型参数的一个例子

// 类型参数 指定并命名一个占位类型, 并且紧随在函数名后面, 使用一对尖括号括起来 (例如 <T> )

// 一旦一个类型参数被指定, 你可以用它来定义一个函数的参数类型, (例如 swapTwoValues(_:_:) 函数中的参数 a 和 b ), 或者作为函数的返回类型,还可以用作函数主体中的注释类型, 在这些情况下, 类型参数会在函数调用时被实际类型所替代

// 你可以提供多个类型参数, 将它们都写在尖括号中, 用逗号隔开

// 命名类型参数

// 在大多数情况下,类型参数具有一个描述性的名字 例如 Dictionary<key,value> 中的 key 和 value, 以及 Array<Element> 中的 Element, 这可以告诉阅读代码的人这些类型参数 和 泛型函数之间的关系, 然而, 当它们之间没有有意义的关系时, 通常使用单个字母命名, 例如 T, U ,V 正如上面演示的 swapTwpValues(_:_:) 函数中的 T 一样

// 注意: 请始终使用大写字母开头的驼峰命名法, 来为类型参数命名, 以表明它们是占位类型, 而不是一个值

// 泛型类型

// 除了泛型函数, Swift 还允许你定义泛型类型, 这些自定义类, 结构体, 和枚举可以适用于任何类型, 类似于 Array 和 Dictionary

struct Stack<Element>{

var items = [Element]()

mutating func push(_ item: Element){

items.append(item)

}

mutating func pop() -> Element{

return items.removeLast()

}

}

// 扩展一个泛型类型

// 当你扩展一个泛型类型的时候, 你并需要在扩展的定义中提供类型的参数列表, 原始类型定义中声明的类型参数列表在扩展中可以直接使用, 并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用

// 下面的例子扩展了泛型类型的 Stack

extension Stack{

var topItem: Element?{

return items.isEmpty ? nil: items[items.count - 1]

}

}

// 类型约束

// swapTwoValues(_:_:) 函数 和 Stack 类型可以作用域任何类型, 不过, 有时候如果能将 使用在泛型函数 和 泛型类型中的类型 添加一个特定的类型约束, 将会是非常有用的, 类型约束可以指定一个类型参数必须继承自指定类型, 或者符合一个特定的协议或协议组合

// 当你创建自定义泛型类型时, 你可以定义你自己的类型约束, 这些约束将提供更为强大的泛型编程能力, 抽象概念,

// 类型约束语法

// 你可以在一个类型参数名后面放置一个类名或者协议名, 并用冒号分隔, 类定义类型约束, 他们将成为类型参数列表的一部分, 对泛型函数添加类型约束的基本语法如下:

// func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U){

//

// }

// 上面这个函数有两个类型参数, 第一个类型参数 T, 有一个要求 T 必须是 SomeClass 子类的类型约束, 第二个类型参数 U, 有一个要求 U 必须缝合 SomeProtocol 协议的类型约束

// 类型约束实践

func findIndex(ofString valueToFind: String, in array: [String]) -> Int?{

for (index, value) in array.enumerated() {

if value == valueToFind {

return index

}

}

return nil

}

// findIndex(ofString:in:) 函数可以用于查找字符串数组中的某个字符串

let strings = ["cat","dog","llama","parakeet","terrapin"]

if let foundIndex = findIndex(ofString: "llama", in: strings) {

print("The index of llama is \(foundIndex)")

}

// 下面展示了 findIndex(ofString:in:) 函数的泛型版本, 不过, 你可以用占位类型 T 替换 string 类型来写出具有相同吧功能的泛型函数 findIndex(_:_:).

// 请注意这个函数返回值的类型仍然是 Int?, 这是因为函数返回的是一个 可选的 索引数, 而不是从数组中得到的一个可选值, 需要提醒的是,这个函数无法通过编译

//func findIndexF<T>(of valueToFind: T, in array:[T]) -> Int?{

//    for (index,value) in array.enumerated() {

//        if value == valueToFind {

//            return index

//        }

//    }

//    return nil

//}

// 上面所写的函数无法通过编译, 问题出在相等性检查上, 即 if value == valueToFind,  不是所有的 Swift 了新鲜感都可以用等式符合 (==) 进行比较, 比如说, 如果你创建一个自定义的类 或结构体来表示一个复杂的数据模型, 那么 Swift 无法猜到对于这个类或结构体而言 '相等' 意味着什么, 正因为如此, 这部分代码无法保证适用于每个可能的类型 T , 所以会出现错误

// 不过这些并不会让我们无法下手, Swift 标准库中定义了一个 Equatable 协议, 该协议要求任何遵循该协议的类型必须实现 等式符号(==) 及不等式符号 (!=). 从而 能对该类型的任意两个值进行比较, 所有的 Swift 标准类型自动支持 Equatable 协议

func findIndexF<T: Equatable>(of valueToFind: T, in array:[T]) -> Int?{

for (index, value) in array.enumerated() {

if value == valueToFind {

return index

}

}

return nil

}

// 关联类型

// 定义一个协议时, 有的时候声明一个 或 多个关联类型作为协议定义 的一部分将会非常有用, 关联类型 为协议何中的某个类型提供了一个占位名(或者说别名), 其代表的实际类型在协议被采纳时才会被指定, 你可以通过 associatedtype 关键字来指定 关联类型

// 关联类型实践

// 下面的例子定义了一个 Container 协议, 该协议定义了一个关联类型 ItemType:

protocol Container{

associatedtype ItemType

mutating func append(_ item: ItemType)

var count: Int { get }

subscript(i: Int) -> ItemType { get }

}

// 为了达到这个目的,Container 协议声明了一个关联类型 ItemType,写作 associatedtype ItemType。这个协议无法定义 ItemType 是什么类型的别名,这个信息将留给遵从协议的类型来提供。尽管如此,ItemType 别名提供了一种方式来引用 Container 中元素的类型,并将之用于 append(_:) 方法和下标,从而保证任何 Container 的行为都能够正如预期地被执行。

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]

}

}

// IntStack 结构体实现了 Container 协议的三个要求,其原有功能也不会和这些要求相冲突。

// 此外,IntStack 在实现 Container 的要求时,指定 ItemType 为 Int 类型,即 typealias ItemType = Int,从而将 Container 协议中抽象的 ItemType 类型转换为具体的 Int 类型。

// 你也可以让泛型 Stack 结构体遵从 Container 协议:

struct Stack2<Element>: Container {

// Stack<Element> 的原始实现部分

var items = [Element]()

mutating func push(_ item: Element) {

items.append(item)

}

mutating func pop() -> Element {

return items.removeLast()

}

// Container 协议的实现部分

mutating func append(_ item: Element) {

self.push(item)

}

var count: Int {

return items.count

}

subscript(i: Int) -> Element {

return items[i]

}

}

// 这一次,占位类型参数 Element 被用作 append(_:) 方法的 item 参数和下标的返回类型。Swift 可以据此推断出 Element 的类型即是 ItemType 的类型。

// 泛型 Where 语句

// 类型约束 让你能够为泛型函数 或泛型类型的类型参数定义一些强制要求

swift 学习- 26 -- 泛型的更多相关文章

  1. Swift学习目录

    本学习基于苹果官方Swift学习材料,保留了原版90%左右的内容(一些项目开发中基本不用的知识点没有整理),并根据理解进行整理.如对原版感兴趣,可以直接单击链接阅读和学习. 第一部分 基础篇 1.基本 ...

  2. 【swift学习笔记】二.页面转跳数据回传

    上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...

  3. 今天开始Swift学习

    今天开始Swift学习  在此记录笔记  以备之后查阅! allenhuang

  4. iOS ---Swift学习与复习

    swift中文网 http://www.swiftv.cn http://swifter.tips/ http://objccn.io/ http://www.swiftmi.com/code4swi ...

  5. 12套swift学习资源分享

    虽然objective-c编程语言在过去很长一段时间都是iOS应用开发的基础语言,且很多iOS开发者对其也深爱有佳,但是随着swift编程语言的问世,迅速发展为开发者追捧的语言.且今年伴随着swift ...

  6. Swift学习之常用UI的使用

    Swift学习之常用UI的使用 最近笔者在开始学习苹果最新的编程语言,因为笔者认为,苹果既然出了这门语言就绝对不会放弃,除非苹果倒闭了(当然这里知识一个玩笑). 所以在不久的将来,swift绝对是iO ...

  7. [转]swift 学习资源 大集合

    今天看到了一个swift的学习网站,里面收集了很多学习资源 [转自http://blog.csdn.net/sqc3375177/article/details/29206779] Swift 介绍 ...

  8. swift 学习资源 大集合

    今天看到一个swift学习网站,其中我们收集了大量的学习资源 Swift 介绍 Swift 介绍 来自 Apple 官方 Swift 简单介绍 (@peng_gong) 一篇不错的中文简单介绍 [译] ...

  9. Swift学习笔记(一)搭配环境以及代码运行成功

    原文:Swift学习笔记(一)搭配环境以及代码运行成功 1.Swift是啥? 百度去!度娘告诉你它是苹果最新推出的编程语言,比c,c++,objc要高效简单.能够开发ios,mac相关的app哦!是苹 ...

随机推荐

  1. MySQL Out-of-Band 攻击

    概述 当涉及到MSSQL与Oracle时,Out-of-Band 注入是非常好的方式.但我注意到MySQL注入的情况并非如此,于是我准备以自己在SQL注入方面的经验做相关的研究.我们可以利用诸如loa ...

  2. 例:判断是不是自有属性hasOwnProperty方法

    自有属性和共有属性: 自有属性:直接保存在对象本地的属性 共有属性:保存在原型对象中,被所有子对象共享的属性 获取时:都可用对象.属性方法 赋值时:自有属性,必须:对象.属性 = 值 共有属性,必须: ...

  3. Lattice

    Lattice是一个无环WFSA,结点可以是hmm状态.hmm(音素).词,每个结点是一段音频在某个时间的对齐 用训练好的声学模型.现成的语言模型和发音字典构建解码网络(wfst),最后将提取的测试集 ...

  4. python练习题1

    1.使用while循环打印输入 1 2 3 4 5 6 8 9 10 num = 1 while num <= 10: if num == 7: num = num + 1 continue e ...

  5. 将sublime添加到鼠标右键

    1.win+R 输入regedit ,打开注册表. 2.找到HKEY_CLASSES_ROOT/*/shell目录,在此目录下 ①新建项,命名为“sublime Text”(也可以是其他名字),双击右 ...

  6. day 3 - 2 数据类型练习

    1.有变量 name = " aleX leNB " 完成如下操作 name = " aleX leNB " # 1) 移除两端空格n1 = name.stri ...

  7. Python 13 简单项目-堡垒机

    本节内容 项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功 ...

  8. JAVA进阶1

    间歇性混吃等死,持续性踌躇满志系列-------------第1天 1.冒泡排序法 import java.util.Arrays; public class SumNum{ public stati ...

  9. android gradle tools 3.X中dependencies, implementation和compile区别

    在3.0版本中,compile 指令被标注为过时方法,而新增了两个依赖指令,一个是implement 和api,这两个都可以进行依赖添加,但是有什么区别呢? api 指令 完全等同于compile指令 ...

  10. Angular 创建组件

    创建组件 0 命令创建 1 创建组件 定义hello.component.ts组件 在app.module.ts中引用并添加到declarations声明中 在app.component.html中使 ...