通过反汇编看一下上面的泛型交换值函数的额原理:
T为Int:
T为Double:
可以看出,虽然泛型的类型不同,但是可以看出两个函数的地址都是0x100001800,调用的同一个函数,可以区分不同类型进行计算,只要是靠metadata也就是元类型来实现的
泛型函数赋值给变量,也是可以说赋值给参数 泛型的举例应用: 类的泛型类泛型的继承:
记住要写泛型<E>
2. 结构体的泛型
因为push会添加元素到数组,pop会删除元素从数组,都会改变内存,所以要加mutating
Score<Int>.grade("A")需要加Int是因为初始化时就要告诉泛型的类型,即使你没调用泛型
使用typealias给关联类型设置真实类型可以省略,因为会根据你传入的参数类型自动识别
也可以传入一个泛型来代替关联类型
针对泛型的类型约束:
protocol Runnable { } class Person { } //要求传入的泛型既是Person或Person 的子类,又要遵守Runnable协议 func swapValues<T: Person & Runnable>(_ a: inout T, _ b: inout T) { (a, b) = (b, a) }针对关联类型的约束:关联类型遵守Equatable协议,泛型也要遵守相同的协议:
protocol Stackable { associatedtype Element: Equatable } class Stack<E: Equatable> : Stackable {typealias Element = E}多个泛型结合where的复杂约束:
S1和S2两个泛型都遵守Stackable协议,并且S1和S2的Element属于同一类型,S1的Element遵守Hashable协议
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable{ return true } var stack1 = Stack<Int>() var stack2 = Stack<Int>() var isEqual = equal(stack1, stack2) print(isEqual) //true但是当stack1和stack2的泛型类型不一致会报错:
由上面的方法可知,get方法需要返回一个遵守Runnable 协议的值,而Person和Car正好都遵守Runnable 协议,所以正确
如果协议中有associatedtype具体报的错误:
报错的原因是这种写法,编译器在编译时期不能确定associatedtype(关联类型)的值
顺便说一句,作为参数会有同样的错误:
1. 使用泛型解决
func get<T : Runnable>(_ type: Int) -> T { if type == 0 { return Person() as! T } return Car() as! T } var r1: Person = get(0) var r2: Car = get(1)成功原因:这种情况下,我们就明确了get(0)的返回值是Person类型,get(1)的返回值是Car类型,这样在编译时期就可以知道相对应的associatedtype(关联类型)的值,所以正确
但是这种写法存在风险,如果你把代码写成var r1:Car = get(0),这样当走入get方法时代码就会变为Person as!Car,这样把Person类型强制转换为Car类型,就会报错的
2. 使用some关键字声明一个不透明类型,不透明类型就是some修饰的返回值只知道是遵守Runnable协议的,具体是什么类型不知道,实际类型的信息更不知道,并且some会强制只有一个返回值类型
这种写法限制了只返回一种类型,这样编译器就会知道返回值类型,从而知道associatedtype(关联类型)的值,所以正确
这时的属性只知道遵守Runnable协议,不知道它的具体类型,更不知道具体类型的属性和方法。