范型(Generics)
- 范型可以將類型參數(shù)化,提高代碼復(fù)用率,減少代碼量
func swapValues<T>(_ a: inout T, _ b: inout T) {
return (a, b) = (a, b)
}
var n1 = 10
var n2 = 20
swapValues(&n1, &n2)
func swapValues<T1, T2, T3>(_ a: T1, _ b: T2, _ c: T3) {
print("");
}
struct Date { var year = 0, month = 0, day = 0 }
var d1 = Date(year: 2023, month: 4, day: 24)
var d2 = Date(year: 1949, month: 10, day: 1)
swapValues(&d1, &d2)
- 范型函數(shù)賦值給變量
func test<T1, T2>(_ v1: T1, _ v2: T2) { }
var fn2:(Int, Double) -> () = test
var fn:(inout Int, inout Int) -> () = swapValues
范型類型
class Stack<E> {
var elements = [E]()
func push(_ element: E) {
elements.append(element)
}
func pop() -> E {
elements.removeLast()
}
func top() -> E {
elements.last!
}
func size() -> Int {
elements.count
}
}
var intStack = Stack<Int>()
var stringStack = Stack<String>()
var anyStack = Stack<Any>()
創(chuàng)建范型類型必須確定是什么類型Stack<Int>(),不像范型函數(shù)那樣傳參指定類型。
- init
帶有初始化器的范型類型中可以去掉<Type>,因為初始化傳參的時候已經(jīng)匹配到類型。
class Stack<E> {
var elements = [E]()
init(firstElement: E) {
elements.append(firstElement)
}
}
var stack = Stack(firstElement: 10)
如果定義為<Double>,與初始化傳參類型不一樣,stack對象也是Double類型,因為Int是可以轉(zhuǎn)換為Int的。
var stack = Stack<Double>(firstElement: 10)//也是<Double>
- 繼承關(guān)系
class SubStack<E> : Stack<E> {
}
- 結(jié)構(gòu)體
struct StructStack<E> {
var elements = [E]()
mutating func push(_ element: E) {
elements.append(element)
}
mutating func pop() -> E {
elements.removeLast()
}
func top() -> E {
elements.last!
}
func size() -> Int {
elements.count
}
}
在結(jié)構(gòu)體實例方法里,修改結(jié)構(gòu)體的內(nèi)存,就需要mutating修飾
如果不加會報錯:Cannot use mutating member on immutable value: 'self' is immutable
- 枚舉
enum Score<T> {
case point(T)
case grade(String)
}
let score1 = Score<Int>.point(100)
let score2 = Score.point(89)
let score3 = Score.point(99.9)
let score4 = Score<Int>.grade("A")
枚舉的范型類型要明確<Type>,否則報錯:Generic parameter 'T' could not be inferred。因為<Type>不明確,無法分配內(nèi)存。即使case用不上也要寫上Score<Int>。
關(guān)聯(lián)類型(Associated Type)
- 關(guān)聯(lián)類型的作用:給協(xié)議中用到的類型定義一個占位名稱。
- 協(xié)議中可以擁有多個關(guān)聯(lián)類型
protocol Stackable {
associatedtype Element //關(guān)聯(lián)類型
mutating func push(_ element: Element)
mutating func pop(_ element: Element)
func top() -> Element
func size() -> Int
}
協(xié)議中想實現(xiàn)范型就用關(guān)聯(lián)類型(associatedtype),代表協(xié)議里面有一個范型類型。
- 使用指定類型實現(xiàn)
class StringStack : Stackable {
//給關(guān)聯(lián)類型設(shè)定真是類型
//typealias Element = String //這句代碼可以省略
var elements = [String]()
//element: String 編譯器自動識別
func push(_ element: String) { elements.append(element) }
func pop(_ element: String) { elements.removeLast() }
func top() -> String { elements.last! }
func size() -> Int { elements.count }
}
var ss = StringStack()
ss.push("jack")
ss.push("Rose")
typealias Element = String這句代碼可以省略不寫,在實現(xiàn)方法中設(shè)定(_ element: String) 編譯器自動識別。
- 使用范型類型實現(xiàn)
class Stack<E> : Stackable {
//typealias Element = E
var elements = [E]()
func push(_ element: E) { elements.append(element) }
func pop(_ element: E) { elements.removeLast() }
func top() -> E { elements.last! }
func size() -> Int { elements.count }
}
類型約束
-
類和協(xié)議的約束
- 要求對范型類型進(jìn)行約束,必須遵守Runnable協(xié)議,
- 并且是Person類型或者Person子類的類型
protocol Runnable { }
class Person { }
func swapValues<T: Person & Runnable>(_ a: inout T, _ b: inout T) {
return (a, b) = (b, a)
}
-
協(xié)議約束
- 要求關(guān)聯(lián)類型必須遵守Equatable協(xié)議
- 在實現(xiàn)的范型類型也必須遵守
protocol Stackable {
associatedtype Element : Equatable //可比較、等價
}
// 必須遵守<E: Equatable>協(xié)議
class Stack<E: Equatable> : Stackable {
typealias Element = E
}
E是范型,也要對E做約束,遵守Equatable協(xié)議,否則編譯器報錯:Type 'Stack<E>' does not conform to protocol 'Stackable'
-
where條件約束
- 要求遵守Stackable協(xié)議
- S1和S2是同一種類型
- S1是默認(rèn)遵守Hashable協(xié)議的
func equal<S1: Stackable, S2: Stackable>(_ s1: S1,_ s2: S2) -> Bool
where S1.Element == S2.Element, S1.Element : Hashable {
return false
}
var s1 = Stack<Int>()
var s2 = Stack<Int>()
var s3 = Stack<String>()
Int默認(rèn)遵守Equatable、Hashable協(xié)議,條件符合。
equal(s1, s2)
s1關(guān)聯(lián)值是Int,s3關(guān)聯(lián)值是String,不是同種類型,條件不符合.
equal(s1, s3)
編譯器報錯:Global function 'equal' requires the types 'Stack<Int>.Element' (aka 'Int') and 'Stack<String>.Element' (aka 'String') be equivalent
不透明類型(Opaque Type)
具有不透明返回類型的函數(shù)或方法會隱藏返回值的類型信息。函數(shù)不再提供具體的類型作為返回類型,而是根據(jù)它支持的協(xié)議來描述返回值。
some
- 使用some關(guān)鍵字聲明一個不透明類型
- some要求只能返回一個類型
- some:除了用在返回值類型上,一般還可以用在屬性上
protocol Runnable {
associatedtype Speed
}
class Dog : Runnable {
typealias Speed = Double
func eat() { }
}
class Person {
var pet: some Runnable {
return Dog()
}
}
var p = Person()
p.pet
1.注意點:下面代碼中r1、r2都是Runnable類型。
protocol Runnable { }
class Person : Runnable { }
class Car : Runnable { }
func get(_ type: Int) -> Runnable {
if type == 0 {
return Person()
}
return Car()
}
var r1 = get(0)
var r2 = get(1)
因為方法中定義返回值Runnable,在編譯時期編譯器認(rèn)為r1、r2是Runnable類型,只有在運行時根據(jù)傳參才決定返回的類型
2.如果協(xié)議中有associatedtype
protocol Runnable {
associatedtype Speed
var speed : Speed { get }
}
class Person : Runnable {
var speed: Double { 0.0 }
}
class Car : Runnable {
var speed: Double { 0 }
var run: Int { 0 }
}
編譯的時候發(fā)現(xiàn)Runnable的關(guān)聯(lián)類型associatedtype Speed是不確定的,所以就產(chǎn)生問題。
func get(_ type: Int) -> Runnable {
if type == 0 {
return Person()
}
return Car()
}
編譯器報錯:Protocol 'RunnableTow' can only be used as a generic constraint because it has Self or associated type requirements
原因:編譯階段只能確定r1是Runnable類型(遵守Runnable),但是它里面關(guān)聯(lián)的Speed具體類型無法確定,所以編譯器就報錯了。
- 解決方案1:通過范型
func get<T: Runnable>(_ type: Int) -> T {
if type == 0 {
return Person() as! T // 強制轉(zhuǎn)換“as! T”有一定的風(fēng)險
}
return Car() as! T
}
var person: Person = get(0)
var car: Car = get(1)
- 解決方案2:通過不透明類型
如果只想返回一個遵守某種協(xié)議的對象,但是這個對象的具體類型不想讓別知道,在別人使用的時候只能看到某種協(xié)議暴露的接口。
func get(_ type: Int) -> some Runnable {
return Car()
}
var p1 = get(0)
p1.speed
var p2 = get(1)
代碼中p1也遵守Runnable協(xié)議,但只能訪問到Runnable協(xié)議的Speed()方法,不能調(diào)用自己的run()方法。
可選項的本質(zhì)
- 可選項的本質(zhì)就是范型枚舉類型
@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ some: Wrapped)
// ......
}
下面定義是等價的
- .some()
var age: Int? = 10
var age0: Optional<Int> = Optional<Int>.some(10)
var age1: Optional = .some(10)
var age2 = Optional.some(10)
var age3 = Optional(10)
age = nil
age3 = .none
- .none
var age: Int? = nil // nil
var age0 = Optional<Int>.none
var age1: Optional<Int> = .none
- 多重可選項
??
var a1 : Int? = 10 //Optional(10)
var a2: Int?? = a1 // Optional(Optional(10))
a2 = nil
var age1 = Optional.some(Optional.some(10)) // Optional(Optional(10))
age1 = .none // nil
var age2: Optional<Optional> = .some(.some(10))
age2 = .none
var age: Int?? = 10 // Optional(Optional(10))
var a : Optional<Optional> = 10 // Optional(Optional(10))
- 可選項的用法:switch
var age: Int? = .some(20)
switch age {
case let value?:
print("some:\(value)")
case nil:
print("none")
}
switch age {
case let .some(value):
print("some:\(value)")
case nil:
print("none")
}