Swift-31-泛型和类型操作

news2024/12/23 16:41:30

泛型

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

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

定义可空类型的泛型变量

let x: Optional<Int> = 3

print(x) //~~ 3

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

enum Optional<Wrapped> {
    case None
    case 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 = T
    
    var 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: Int
    let y: Int
    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 noAbEqual = (a != b) //~~ false

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

实现Comparable协议

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

//自定义的Point结构体实现Comparable
struct Point: Comparable { //因为继承的原因,所以这块不需要写成Equatable,Comparable 
    let x: Int
    let y: Int
    
    static 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) //false

let c = Point(x: 2, y: 6) //false
let d = Point(x: 3, y: 7) //false

let cdEqual = (c == b) //false
let cLessThanD = (c < d) //true

let cLessThanEqualD = (c <= d) //true
let cGreaterThanD = (c > d) //false
let cGreaterThanEqualD = (c >= d) //false

在这里插入图片描述

自定义运算符

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

定义自定义类

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

添加自定义运算符

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

//声明一个新运算符
infix operator +++
func +++(lhs: Person, rhs: Person) {
    lhs.spouse = rhs
    rhs.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.coloradmin.cn/o/1627873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

11.JAVAEE之网络原理1

1.应用层(和程序员接触最密切) 应用程序 在应用层这里,很多时候, 都是程序员"自定义"应用层协议的,(当然,也是有一些现成的应用层协议)&#xff08;这里的自定义协议,其实是非常简单的~~协议 >约定,程序员在代码中规定好,数据如何进行传输) 1.根据需求, 明确要传…

使用mapinfo软件的在线地图插件运行错误解决

使用mapinfo软件的在线地图插件运行错误解决 一、如何解决win10/win11家庭版运行MapInfo中的在线地图插件报错【unexpected error&#xff1b;quitting】问题&#xff1f;二、如何解决在线地图切换地图源时的报错问题&#xff1f; 一、如何解决win10/win11家庭版运行MapInfo中的…

C# WinForm —— 10 单选按钮与复选框的介绍与使用

单选按钮 RadioButton 一组单选按钮中&#xff0c;只能选择一个&#xff0c;互相排斥 常用属性、事件&#xff1a; 属性用途(Name)单选按钮的ID&#xff0c;在代码里引用的时候会用到,一般以 rb开头Text单选按钮旁边显示的 文本信息Checked单选按钮的勾选状态Appearance控制单…

JetBrains CLion v2023.3.4 激活版 (C/C++ 集成开发IDE)

前言 JetBrains CLion是一款跨平台的C/C集成开发环境&#xff0c;由JetBrains公司推出。其最新版本支持C14几乎完全&#xff0c;并初步支持C17&#xff0c;使得编写代码更加便捷。CLion还提供了Disassembly view&#xff08;反汇编视图&#xff09;&#xff0c;即使没有源代码…

Unity类银河恶魔城学习记录15-1,2 p153 Audio Manager p154 Audio distance limiter

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili AudioManager.cs using System.Collections; using System.Collections.Gen…

Python游戏工具包pygame

当你涉及游戏开发时&#xff0c;Pygame是一个强大的工具包&#xff0c;它提供了一系列功能丰富的模块和工具&#xff0c;让你可以轻松地创建各种类型的游戏。在本文中&#xff0c;我将介绍Pygame的依赖以及其详细属性&#xff0c;同时提供一些示例代码来说明其用法。 目录 一…

关于discuz论坛网址优化的一些记录(伪静态)

最近网站刚上线&#xff0c;针对SEO做了些操作&#xff0c;为了方便网站网页被收录&#xff0c;特此记录下 1.开启伪静态 按照操作勾选所有项&#xff0c;然后点击查看伪静态规则 2.打开宝塔&#xff0c;找到左侧列表的网站&#xff0c;然后找到相应站点的设置。把discuz自动…

1、Qt简介

文章目录 前言一、pySide2 / pySide6 ,PyQt5 / PyQt6二、安装包1 安装pyside22 安装pyqt5三、从一个简单的例子开始三、界面动作处理---信号(signal)与槽(slot)(Qt最核心的机制)--- 绑定事件封装到类中总结前言 参考文章:Qt简介 本文开始就开始进入到qt的开发笔记书写…

前缀和、差分模板(C++ 一维、二维)

前缀和&#xff08;Prefix Sum&#xff09;和差分&#xff08;Difference&#xff09;是两种常用的数组操作&#xff0c;用于解决一些求和、求区间和、区间修改等问题。 前缀和是指一个数组中每个位置上的元素与它前面位置上所有元素的和。例如&#xff0c;数组nums[1, 2, 3, 4…

打造智能语音机器人-用语音控制机器人

人工智能现已成为国家发展重大战略&#xff0c;智能语音技术作为人工智能产业链上的关键一环&#xff0c;AI应用成熟的技术之一&#xff0c;人工智能的发展也进入了一个崭新的阶段。那么打造智能语音机器人怎样实现用语音控制机器人呢&#xff1f;和小编一起来看看。 选择合适的…

【OceanBase诊断调优 】—— 如何快速定位SQL问题

作者简介&#xff1a; 花名&#xff1a;洪波&#xff0c;OceanBase 数据库解决方案架构师&#xff0c;目前负责 OceanBase 数据库在各大型互联网公司及企事业单位的落地与技术指导&#xff0c;曾就职于互联网大厂和金融科技公司&#xff0c;主导过多项数据库升级、迁移、国产化…

内推米哈游(原神),38岁被拒。。。

内推米哈游&#xff0c;被拒 今天逛职场 App 无意看到一条和米哈游&#xff08;原神&#xff09;相关的帖子。 楼主说前同事跳槽去了米哈游&#xff0c;因为之前合作过&#xff0c;彼此熟悉&#xff0c;主动联系自己说&#xff0c;想要帮忙内推。 当时 38 岁的楼主&#xff0c;…

【Linux系统编程】基本指令(一)

目录 1、ls指令 2、pwd指令 3、cd指令 4、touch指令 5、mkdir指令 6、rmdir指令与rm指令 7、man指令 8、cp指令 指令就像刚学windows时学的单击、双击一样&#xff0c;都是操作操作系统 在windows下&#xff0c;文件通常会放在文件夹中&#xff0c;而Linux中同样有文件…

vue 项目关于不同分辨率的电脑网页适配方案

流式布局&#xff1a;这是一种相对灵活的布局方式&#xff0c;页面的元素宽度使用相对宽度&#xff08;例如百分比&#xff09;来定义&#xff0c;而不是使用绝对宽度&#xff08;例如像素&#xff09;。这样&#xff0c;当浏览器窗口大小变化时&#xff0c;元素会自动调整大小…

MySQL B+索引的工作原理及应用

引言 在数据库系统中&#xff0c;索引是优化查询、提高性能的关键技术之一。特别是在MySQL数据库中&#xff0c;B树索引作为最常用的索引类型&#xff0c;对数据库性能有着至关重要的影响。本文旨简单解析MySQL中B树索引的工作原理&#xff0c;帮助学生朋友们更好地理解和利用…

axios——503响应超时重复多次请求——技能提升

今天在写后台管理系统时&#xff0c;遇到一个问题&#xff0c;就是每天早上一启动项目&#xff0c;接口会提示503超时&#xff0c;因此项目运行必须重新刷新请求成功后才可以正常使用。 后端同事说请求超时了&#xff0c;需要前端处理一下&#xff0c;如果是503的状态码&#…

GEM TSU Interface Details and IEEE 1588 Support

摘要&#xff1a;Xilinx ZNYQ ULTRASCALE MPSOC的GEM和1588的使用 对于FPGA来说&#xff0c;只需要勾选一些znyq的配置就行了&#xff0c;其余的都是软件的工作&#xff1b; 所有配置都勾选之后&#xff0c;最终会露出来的接口如下&#xff1a; GEM需要勾选的配置如下&#xf…

如何在CentOS本地搭建DataEase数据分析服务并实现远程查看数据分析

文章目录 前言1. 安装DataEase2. 本地访问测试3. 安装 cpolar内网穿透软件4. 配置DataEase公网访问地址5. 公网远程访问Data Ease6. 固定Data Ease公网地址 前言 DataEase 是开源的数据可视化分析工具&#xff0c;帮助用户快速分析数据并洞察业务趋势&#xff0c;从而实现业务…

Python基础10-使用正则表达式进行文本处理

在编程过程中&#xff0c;我们经常需要对文本进行处理&#xff0c;以提取、替换或分割特定的字符串。正则表达式&#xff08;Regular Expression&#xff09;是一种强大的文本处理工具&#xff0c;它可以帮助我们实现这些任务。以下是使用正则表达式进行文本处理的一些基本方法…

羊大师讲解五一假期,探索羊奶的健康奥秘

羊大师讲解五一假期&#xff0c;探索羊奶的健康奥秘 随着五一假期的到来&#xff0c;许多人选择出游、休息或与家人共度美好时光。在这个特别的时刻&#xff0c;我们或许可以停下来&#xff0c;探索一种营养丰富、历史悠久的饮品——羊奶。 羊奶&#xff0c;作为大自然赐予我…