swift -- 泛型(Generics)

238 阅读3分钟

泛型参数化

  • 泛型可以将类型参数化,提高代码复用率,减少代码量
func swapValues<T> (_ a: inout T,_ b:inout T) {
    (a,b) = (b,a)
}

var num1:Int = 10
var num2:Int = 20
swapValues(&num1,&num2)
print(num1,num2)

struct Date {
    var year = 0,month = 0,day = 0
}

var date1 = Date(year:2019,month:9,day:19)
var date2 = Date(year:2020,month:10,day:20)
swapValues(&date1,&date2)
print(date1,date2)

  • 泛型函数赋值给变量
func test<T1,T2> (_ v1:T1,_ v2:T2){}
var fn :(Int,Double) -> () = test //注意点 函数类型
//类泛型
class Stack<E> {
    var array = [E]()
    func push(_ element:E) { array.append(element) }
    func pop() -> E { array.removeLast() }
    func top() -> E { array.last!}
    func size() -> Int { array.count }
}

//结构体泛型
struct Stack<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 }
}


var statck = Stack<Int>()
statck.push(12)
statck.push(13)
statck.push(14)
print(statck.top())
print(statck.size())
print(statck.pop())
print(statck.array)

//枚举泛型
enum Score<T> {
case point(T)
case grade(String)
}
let score0 = Score<Int>.point( 100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score<Int>.grade("A")

关联类型(Associated Type)

  • 关联类型的作用:给协议中用到的类型定义一个占位名称,协议不能用泛型
  • 协议中可以拥有多个关联类型
protocol Stackable {
    associatedtype Element
    func push(_ element:Element)
    func pop() -> Element
    func top() -> Element
    func size() -> Int
}

class Stack: Stackable {
    // 给关联类型设定真实类型,也可以省略不写,编译器会推导出类型
    typealias Element = String
    var array = [String]()
    func push(_ element:String) { array.append(element) }
    func pop() -> String { array.removeLast() }
    func top() -> String { array.last!}
    func size() -> Int { array.count }
}
var ss = Stack()

class Stack<E>: Stackable {
//  typealias Element = String
    var array = [E]()
    func push(_ element:E) { array.append(element) }
    func pop() -> E { array.removeLast() }
    func top() -> E { array.last!}
    func size() -> Int { array.count }
}

var s = Stack<String>()
s.push("Jack")
s.push("Por")
print(s.array)

类型约束

protocol Runnable { }
class Person:Runnable { }

//泛型约束
func swapValues<T: Person & Runnable > (_ a :inout T,_ b :inout T) {
    (a,b) = (b,a)
}

var p = Person()
var p1 = Person()
swapValues(&p,&p1)

//约束2

protocol Stackable {
    associatedtype element:Equatable
}

class Stack<E:Equatable>:Stackable {
    typealias element = E
}

//S1.element 表达的是关联类型
func equal<S1:Stackable,S2:Stackable>(_ s1:S1, _ s2:S2) -> Bool where
S1.element == S2.element,S2.element : Hashable
{//关联类型必须相同
    return false
}

var stack1 = Stack<Int>()//Int 默认遵守Equatable,Hashable协议
var stack2 = Stack<String>()
// error: requires the types 'Int' and 'String' be equivalent
print(equal(stack1, stack2))

协议类型的注意点

protocol Runnable { }
class Person: Runnable { }
class Car: Runnable { }

func get(_ type: Int) -> Runnable {
    if type == 0 {
       return Person()
    }
    return Car()
}

//这个不会报错
var r1:Person = get(0)
var r2:Car = get(1) 


===========如果协议中有associatedtype===============

protocol Runnable {
    associatedtype Speed
    var speed:Speed {  get }
}
    
class Person: Runnable {
    var speed: Double {  0.0 }
}

class Car: Runnable {
    var speed: Int {  0 }
}

//Protocol 'Runnable' can only be used as a generic constraint because it has Self or associated type requirements
//报错的原因是 ,函数返回两个值 无法确定关联值的类型 处理方式 使用泛型
func get(_ type: Int) -> Runnable {
    if type == 0 {
       return Person()
    }
    return Car()
}


//处理方式一 存在一点风险性
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)

//处理方式2 使用some关键字声明一个不透明类型 
//some关键字 不透明处理类型,缺点:只能返回一种类型  优点:屏蔽类 内部实现,只让外部知道实现了协议里的内容
func get(_ type: Int) -> some Runnable{
    return Car()
}
var r1 = get(0)
var r2 = get(1) //这个不会报错

some

  • some除了用在返回值类型上,一般还可以用在属性类型上
  • some 就是用来解决协议里使用大写的Self 和关联类型

使用场景: 返回遵守某个协议的类型,不想暴露协议的类型

//some  就是用来解决大写的Self 和关联类型
protocol Runnable {
    associatedtype  Speed
}

class Dog: Runnable {
    typealias Speed = Double
}

class Person {
    var pet: some Runnable {
        Dog()
    }
}