Swift-31-泛型和类型操作

泛型

Swift泛型(generics) 让我们写出的类型和函数可以使用对于我们或编译器都未知的类型。 很多内建类型(包括可空类型、数组和字典)都是用泛型实现的,比如数组和一些集合就是用泛型方式来实现的。

一种运行时进行类型检查的技术,效率高但是不安全。在swift中泛型可用于结构体、类以及函数和方法

定义可空类型的泛型变量

let x: Optional<Int> = 3print(x) //~~ 3

上述代码实际上用到了系统提供的协议,具体的在后续详细讲解,这里只了解其用法就可以了

enum Optional<Wrapped> {case Nonecase Some(Wrapped)
}

定义泛型类型

语法:class/struct Name<Type>,上述尖括号中的Type是一个占位符,也可换成其它名称,比如T,在实例化时会换成实际的值。

比如一个简单的堆栈实现

//Element是一个占位符,也可换成其它名称
struct Stack<T>: Sequence { var items = [T]()mutating func push(_ newItem: T) {items.append(newItem)}mutating func pop() -> T? {guard !items.isEmpty else {return nil}return items.removeLast()}
}//~~~使用,在代码运行时,泛型T会换成Int
var stack = Stack<Int>()    

定义泛型函数和方法

函数和类的方法的返回值和参数也可以用泛型来代码,比如下拉代码声明了一个泛型函数。
在这里插入图片描述

    func myMap<T,U> ( _ items:[T], _ f:(T)->(U) )  -> [U]{var result = [U]()for item in items{result.append( f(item) )}return result}

方法测试

let string = ["one", "two", "three"]
let stringLen = myMap(string){$0.count} //第一个cod为一个函数
print(stringLen) //~~ [3,3,5]

给泛型占位符设置约束条件

泛型的比较,必须要把泛型标识符声明为系统提供的Equatable类型。

func checkIfEqual<T: Equatable>(_ first: T, _ second: T) -> Bool {return first == second
}
print(checkIfEqual(1, 1)) //~~true
print(checkIfEqual("a string", "a string")) //~~true
print(checkIfEqual("a string", "a different string")) //~~false

多个约束符的例子,下例表示用CustomStringConvertible保证了first和second都有返回字符串的属性description。

func checkIfDescriptionsMatch<T: CustomStringConvertible, U: CustomStringConvertible>(_ first: T, _ second: U) -> Bool {return first.description == second.description
}print(checkIfDescriptionsMatch(Int(1), UInt(1)))
print(checkIfDescriptionsMatch(1, 1.0))
print(checkIfDescriptionsMatch(Float(1.0), Double(1.0)))

泛型与协议

协议是不可以直接使用泛型的,如果想在协议中使用泛型,可以使用一个叫“关联类型”的特性。用到关键字 associatedtype来修饰协议属性,比如系统提供的IteratorProtocol协议就是如下定义的:

protocol IteratorProtocol {associatedtype Element mutating func next() -> Element?
}

上述associatedtype Element表示符合这个协议的类型必须提供具体类型做为Element类型。符合这个协议的类型应该在其定义为内部为Element提供typealias定义,那么就可以按如下方式使用了:

//用 StackIterator 把Stack封装起来。
struct StackIterator<T>: IteratorProtocol {typealias Element = Tvar stack: Stack<T>mutating func next() -> Element? {return stack.pop()}
}

这段代码可以借助Swift类型推断功能,简写为如下形式:

struct StackIterator<T>: IteratorProtocol {var stack: Stack<T>mutating func next() -> T? {return stack.pop()}
}

使用

var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
var myStackIterator = StackIterator(stack: myStack)
while let value = myStackIterator.next() {print("got \(value)")
}

where子语句

用占位类型s把pushAll(_:)变成泛型方法,它是符合Sequence协议的类型。S的约束保证我们可以用for-in语法循环遍历之。不过,这还不够。为了把从sequence中取出的数据项推入栈,需要确保从序列类型中来的数据项类型和栈元素的类型匹配。也就是说,还需要一个约束让S所产生元素的类型是Element。

    //where语句相当于一个过滤器mutating func pushAll<S: Sequence>(_ sequence: S) where S.Iterator.Element == Element {for item in sequence {self.push(item)}}

附:Stack 示例实现

struct Stack<Element>: Sequence {var items = [Element]()mutating func push(_ newItem: Element) {items.append(newItem)}mutating func pop() -> Element? {guard !items.isEmpty else {return nil}return items.removeLast()}func map<U>(_ f: (Element) -> U) -> Stack<U> {var mappedItems = [U]()for item in items {mappedItems.append(f(item))}return Stack<U>(items: mappedItems)}// Sequence 协议的方法func makeIterator() -> StackIterator<Element> {return StackIterator(stack: self)}mutating func pushAll<S: Sequence>(_ sequence: S) where S.Iterator.Element == Element {for item in sequence {self.push(item)}}
}

类型操作

值的比较

类型的比较在很多场景下都有需求,在Swift中可通过实现Equatable和Comparable这两个协议来实现。

实现Equatable协议

struct Point: Equatable {let x: Intlet y: Intstatic func == (lhs: Point, rhs: Point) -> Bool {return (lhs.x == rhs.x) && (lhs.y == rhs.y)}
}
let a = Point(x: 3, y: 4)
let b = Point(x: 3, y: 4)
let abEqual = (a == b) //~~ true
let noAbEqual = (a != b) //~~ false

上述代码中==(中缀运算符)被声明为了static方法,事实上==是定义在全局范围内的。

实现Comparable协议

Comparable会提供更多的功能,因为Comparable继承了Equatable。

//自定义的Point结构体实现Comparable
struct Point: Comparable { //因为继承的原因,所以这块不需要写成Equatable,Comparable let x: Intlet y: Intstatic func ==(lhs: Point, rhs: Point) -> Bool {return (lhs.x == rhs.x) && (lhs.y == rhs.y)}static func <(lhs: Point, rhs: Point) -> Bool {return (lhs.x < rhs.x) && (lhs.y < rhs.y)}
}let a = Point(x: 3, y: 4)
let b = Point(x: 3, y: 4)let abEqual = (a == b) //true
let abNotEqual = (a != b) //falselet c = Point(x: 2, y: 6) //false
let d = Point(x: 3, y: 7) //falselet cdEqual = (c == b) //false
let cLessThanD = (c < d) //truelet cLessThanEqualD = (c <= d) //true
let cGreaterThanD = (c > d) //false
let cGreaterThanEqualD = (c >= d) //false

在这里插入图片描述

自定义运算符

Swift允许开发者创建自定义运算符。这个特性意味着我们可以创建自己的运算符来表示两个Person的实例结婚了。自定义运算符不太建议使用,因为它也只限于数学运算范围内,正常情况下使用系统提供的就够了。

定义自定义类

class Person: Equatable {var name: Stringvar age: Intweak var spouse: Person?init(name: String, age: Int) {self.name = nameself.age = age}func marry(_ spouse: Person) {self.spouse = spousespouse.spouse = self}
}

添加自定义运算符

在Person类声明的外面添加以下代码,以添加自定义运算符。

//声明一个新运算符
infix operator +++
func +++(lhs: Person, rhs: Person) {lhs.spouse = rhsrhs.spouse = lhs
}

添加自定义运算符到默认组

precedencegroup Marriage {associativity: none  //这是一个运算优先级定义
}

如果没有上述代码,则因为swift内部对新添加的运算符默认添加到swift内部默认的组为DefaultPrecedence。

使用自定义运算符

let drew = Person(name: "Drew", age: 33)
let matt = Person(name: "Matt", age: 32)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/652565.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA前端快速入门基础_javascript入门(01)

写在前面:本文用于快速学会简易的JS&#xff0c;仅做扫盲和参考作用 1.JS是什么 JavaScript是一门跨平台&#xff0c;面向对象的脚本语言(即不需要编译&#xff0c;可以直接通过浏览器进行解释)。JS和Java是两门完全不相同的语言&#xff0c;但是基础的语法是类似的 2.JS的引…

RK3568 学习笔记 : busybox 制作 ext4最小根文件系统

前言 开发板型号&#xff1a; 【正点原子】 的 RK3568 开发板 AtomPi-CA1 使用 VMware 虚拟机 ubuntu 20.04 编译 busybox&#xff0c;并制作 emmc 中的 ext4 根文件系统 rootfs 下载 busybox 可以在 https://busybox.net/downloads/snapshots/ 下载最新的 busybox&#xff…

Linux实验一:Linux环境及编程工具

目录 一、实验目的二、实验内容三、参考代码四、实验步骤步骤1. 编辑源代码test1.c步骤2. 编译源代码test1.c步骤3. 调试test1步骤4. 重新编译运行test1.c 五、实验结果六、实验总结 一、实验目的 1、掌握Linux C开发过程中的基本概念&#xff1b; 2、掌握如vim&#xff0c;GC…

leetcode和相关题目

1. 两数之和 直接利用hashmap存储值和对于索引&#xff0c;利用target-nums[i]去哈希表里找对应数值。返回下标。 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> mp;vector<int> res;fo…

CentOS安装SonarQube

系列文章目录 文章目录 系列文章目录前言前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 sonar是一款静态代码质量分析工具,支持Java、Python、PHP、JavaScript、…

一分钟理解:比特币第一次严重漏洞,生产1844亿枚比特币!是如何发生的

1、事件 2010年8月15日&#xff08;也就是比特币诞生的第二年&#xff0c;创世区块于2009年1月3日诞生&#xff09;&#xff0c;有人发现&#xff0c;在比特币区块链的第74638块上&#xff0c;一笔让人惊愕的交易。 这笔交易出现了184 467 440 737.09551616个比特币&#xff…

C# WinForm —— 08 Form初始化、布局、注册事件

Form 初始化 Form初始化的时候会调用 Designer.cs 里的 InitializeComponent(); 函数&#xff0c;在InitializeComponent(); 函数里面有Load Form语句时会调用 FrmLogin_Load()函数 Form布局 两种方式&#xff1a; 拖控件到窗体&#xff0c;设置属性在Load事件中写代码添加…

算法 || 二分查找

目录 二分查找 在排序数组中查找元素的第一个和最后一个位置 搜索插入位置 一个数组经过划分后具有二段性的都可以用二分查找 二分查找 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; ​ 暴力解法&#xff1a;直接遍历数组&#xff0c;找到 target 便返回下标&am…

基于微信小程序云开发实现考研题库小程序V2.0

不久之前&#xff0c;基于云开发的微信答题小程序搭建题库小程序V1.0&#xff0c;软件架构是微信原生小程序云开发。现在来回顾一下&#xff0c;已经实现的功能。 一、V1.0项目预览 1、页面结构 首页 答题页 结果页 我的页 排行榜页 答题历史页 登录页 使用指引页 2…

LeetCode57. 插入区间

LeetCode57.插入区间 题目思路: 代码 /* 前置知识&#xff1a; vector<vector<int>> a,b; 二维vector数组是可以将二维中的一维vector数组给push_back的&#xff0c; 不是只有单个元素才可以&#xff0c;整个一维的vector数组也可以 b[0] {1,2,3},b[1] {4,5,6}…

如何消除浏览器SmartScreen对网站“不安全”提示?

面对互联网时代用户对网站安全性和可信度的严苛要求&#xff0c;网站运营者时常遭遇Microsoft Defender SmartScreen&#xff08;SmartScreen&#xff09;提示网站不安全的困扰。本文将剖析SmartScreen判定网站不安全的原因&#xff0c;并为运营者提供应对策略&#xff0c;以恢…

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…