swift泛型基础使用

关于泛型的一点理解

泛型在实际使用中,实际上就是一个占位符,只要能够作为函数参数使用的类型都可以用作占位符。如class, struct, 枚举, 闭包,函数,协议类型都可以作为泛型具体化使用。

struct Human {
    func sayHello() {
        print("Hello World!")
    }
}

enum WeekDays {
    case Sunday
    case Monday
}

protocol HumanType {
    func canEat()
}

/// 占位符为Int, String
Human.init().sayHello()
/// 占位符为NSObject, 泛型集合
Human>.init().sayHello()
/// 占位符为Any, Human 泛型类型
Human>.init().sayHello()
/// 占位符为闭包/函数, 枚举
Human<(Int) -> Any, WeekDays>.init().sayHello()
/// 占位符为Any, 协议类型
Human.init().sayHello()

输出结果如下:
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!

泛型在具体的调用使用时,如生成一个实例,或者函数调用时,就需要对持有的泛型具体化。

struct Human {
    func sayHello() {
        print("Hello World!")
    }
}

struct Superman {
    var name: T
    var ablility: U
    func canFly() {
        print("Flying")
    }
}

Human.init().sayHello()
Superman.init(name: "Sunwukong", ablility: "jindouyun").canFly()

类和结构体的具体化时有两种方式,一种就是直接指定, 如Human这种;另外一种就是在初始化时,由初始化参数指定,如Superman.init(name: "Sunwukong", ablility: "jindouyun")

在swift中,泛型使用广泛,如类,struct, 枚举,协议,函数。具体来说一般是如下这些地方: 类的属性,struct属性, 枚举的关联属性,协议的关联类型,函数参数/返回值。下面分别进行一下介绍。
其中struct中的使用,前面的代码中已经展示了,不再赘述。

类中使用

泛型在类中的使用,主要指的是在属性中的使用。当然没有属性也是可以的,就和前面struct的例子一样。如

class MyGeneric {
    func sayHello() {
        print("Hello World!")
    }
}

MyGeneric.init().sayHello()

如果没有初始化方法指定泛型(也就是init方法) 这时, 必须使用<>专门指定。如

MyGeneric.init()

一般的在属性中使用的情况如下

class Person {
    var name: T
    var age: U

    init(name: T, age: U) {
        self.name = name
        self.age = age
    }
}

class Teacher: Person {
    var major: V
    init(name: T, age: U, major: V) {
        self.major = major
        super.init(name: name, age: age)
    }
}

class Student: Person {
    var grade: V
    init(name: String, age: Int, grade: V) {
        self.grade = grade
        super.init(name: name, age: age)
    }
}

class ProStudent: Student {
    var pro: O?
    override init(name: String, age: Int, grade: V) {
        super.init(name: name, age: age, grade: grade)
    }
}

ProStudent.init(name: "xiaoli", age: 11, grade: 3).sayHello()

对以上代码做一下说明。

  1. class的泛型可以在继承时直接具体化。如Student,对于继承的Person的两个泛型指定了String, Int两个具体类型。如:
class Student: Person
  1. 也可以在继承时仍然不具体化,这时把Person的泛型,仍然作为泛型使用,如Teacher类中,仍然作为泛型使用,就是需要把Person的两个泛型,再拿到Teacher中。
class Teacher: Person
  1. 可选属性为泛型时,因为可以不用在init方法中初始化,在实例化对象时,就需要专门外面进行指定它的泛型是什么,如ProStudent就需要指定泛型O具体是什么类型
ProStudent.init(name: "xiaoli", age: 11, grade: 3).sayHello()

枚举中使用

枚举中泛型的使用,主要是关联属性中的使用

enum Event {
    case next(T)
    case error(Swift.Error)
    case complete
    func sayHello() {
        print("Hello World!")
    }
}

Event.next("do something").sayHello()

next含有关联属性T, 为泛型

函数中使用

函数中使用泛型时,主要用在参数和返回值。如果参数中有给出所有的函数的泛型具体化时,那么可以直接调用。如果参数中并没有完全给出所有的泛型,那么需要在返回值类型中进行指定。如下

struct Human {
    func sayHello() {
        print("Hello World!")
    }
    
    func play(type: Type) -> Type {
        print("play type: \(type)")
        return type
    }
}

struct Superman {
    var name: T
    var ablility: U
    func canFly() {
        print("Flying")
    }
    
    func play(type: Type) -> Type {
        print("play type: \(type)")
        return type
    }
    
    func getHuman() -> Human {
        return Human.init()
    }
}

Superman.init(name: "Sunwukong", ablility: "jindouyun").play(type: 2)
let human: Human = Superman.init(name: "Sunwukong", ablility: "jindouyun").getHuman()
human.sayHello()

其中Superman的play函数调用时,参数为type的泛型和返回值相同,所以参数已经覆盖了所用到的所有的泛型。那么可以直接调用

Superman.init(name: "Sunwukong", ablility: "jindouyun").play(type: 2)

而getHuman这个泛型函数,没有参数,也就不能通过参数给出泛型,那么需要在返回值的地方进行指定

let human: Human = Superman.init(name: "Sunwukong", ablility: "jindouyun").getHuman()
human.sayHello()

在返回值的地方进行指定往往有点不方便, 因为我们习惯于函数的调用直接返回想要的结果,我们对上面这种情况稍作改造,如下

struct Superman {
    var name: T
    var ablility: U
    func canFly() {
        print("Flying")
    }
    
    func play(type: Type) -> Type {
        print("play type: \(type)")
        return type
    }
    
    func getHuman(typeA: A.Type, typeB: B.Type) -> Human {
        return Human.init()
    }
}

Superman.init(name: "Sunwukong", ablility: "jindouyun").play(type: 2)
let human = Superman.init(name: "Sunwukong", ablility: "jindouyun").getHuman(typeA: Int.self, typeB: Int.self)
human.sayHello()

可以看到, 现在let human已经不需要再指定返回值类型了,直接有函数参数推倒就可以了。总结,函数中泛型使用时:

  1. 一般说来,函数中用的泛型尽量在参数中直接指定完毕,如果没有实例参数指定,也可以指定类型,就如上面的A.Type, B.Type
  2. 函数的泛型和类和struct中的属性泛型可以毫无关系。函数中的泛型是独立的,如上面getHuman中的泛型和Superman中的T, U没有任何关系。当然也可以是同一个,同一些。

协议中使用

泛型在协议中使用主要指的是关联类型的使用。使用associatedtype 标记一个类型,即为协议中的泛型。如下面代码,存储和保存一个元素。where语句是给泛型添加一个类型约束。关于泛型的类型约束,后面还会探讨。

protocol StoreOperator {
    associatedtype ELEMENT where ELEMENT: Codable
    func save(ele: ELEMENT, key: String)
    func get(key: String) -> ELEMENT?
}

struct UserInfo: Codable {
    var name: String
    var age: Int
}

struct UserInfoManager {
    static let shared = UserInfoManager.init()
    private init(){}
}

extension UserInfoManager: StoreOperator {
    func save(ele: ELEMENT, key: String) {
        guard let data = try? JSONEncoder().encode(ele) else { return }
        UserDefaults.standard.setValue(data, forKey: key)
    }
    func get(key: String) -> ELEMENT? {
        guard let data = UserDefaults.standard.value(forKey: key) as? Data else { return nil }
        let userInfo = try? JSONDecoder().decode(ELEMENT.self, from: data)
        return userInfo
    }
    typealias ELEMENT = UserInfo
}

let userInfo = UserInfo.init(name: "xiaoli", age: 18)

UserInfoManager.shared.save(ele: userInfo, key: "xiaoli")
if let xiaoliInfo = UserInfoManager.shared.get(key: "xiaoli") {
    print("xiaoli info is : \(xiaoliInfo)")
}

协议中的泛型,在使用满足协议的时候,必须通过typealias给出具体类型。
有一点需要说明的是协议中的泛型,在具体化时并不能够指定为一个协议类型。这个和struct, class, enum, 函数中可以使用协议类型具体化是不同的。
枚举中使用协议类型具体化,HumanType是协议类型,如下:

protocol HumanType {
    func canEat()
}
enum Event {
    case next(T.Type)
    case error(Swift.Error)
    case complete
    func sayHello() {
        print("Hello World!")
    }
}

Event.next(HumanType.self).sayHello()

函数中使用协议类型具体化,HumanType是协议类型,如下:

struct Human {
    func sayHello() {
        print("Hello World!")
    }

    func play(type: Type) -> Type {
        print("play type: \(type)")
        return type
    }
}
struct Superman {
    var name: T
    var ablility: U
    func canFly() {
        print("Flying")
    }

    func play(type: Type) -> Type {
        print("play type: \(type)")
        return type
    }

    func getHuman(typeA: A.Type, typeB: B.Type) -> Human {
        return Human.init()
    }
}
protocol HumanType {
    func canEat()
}
let human = Superman.init(name: "Sunwukong", ablility: "jindouyun").getHuman(typeA: Int.self, typeB: HumanType.self)
human.sayHello()

泛型的使用非常广泛,此篇为基础使用,尤其是泛型和协议结合使用会更加灵活多变。不对之处,欢迎指正。

你可能感兴趣的:(swift泛型基础使用)