swift泛型基础使用

关于泛型的一点理解

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

struct Human<T, U> {
    func sayHello() {
        print("Hello World!")
    }
}

enum WeekDays {
    case Sunday
    case Monday
}

protocol HumanType {
    func canEat()
}

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

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

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

struct Human<T, U> {
    func sayHello() {
        print("Hello World!")
    }
}

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

Human<String, Int>.init().sayHello()
Superman.init(name: "Sunwukong", ablility: "jindouyun").canFly()

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

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

类中使用

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

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

MyGeneric<Any>.init().sayHello()

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

MyGeneric<Any>.init()

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

class Person<T, U> {
    var name: T
    var age: U

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

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

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

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

ProStudent<Int, Any>.init(name: "xiaoli", age: 11, grade: 3).sayHello()

对以上代码做一下说明。

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

枚举中使用

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

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

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

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

函数中使用

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

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

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

Superman.init(name: "Sunwukong", ablility: "jindouyun").play(type: 2)
let human: Human<Int, Int> = 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<Int, Int> = Superman.init(name: "Sunwukong", ablility: "jindouyun").getHuman()
human.sayHello()

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

struct Superman<T, U> {
    var name: T
    var ablility: U
    func canFly() {
        print("Flying")
    }
    
    func play<Type>(type: Type) -> Type {
        print("play type: \(type)")
        return type
    }
    
    func getHuman<A, B>(typeA: A.Type, typeB: B.Type) -> Human<A, B> {
        return Human<A, B>.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<T> {
    case next(T.Type)
    case error(Swift.Error)
    case complete
    func sayHello() {
        print("Hello World!")
    }
}

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

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

struct Human<T, U> {
    func sayHello() {
        print("Hello World!")
    }

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

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容