关于泛型的一点理解
泛型在实际使用中,实际上就是一个占位符,只要能够作为函数参数使用的类型都可以用作占位符。如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
在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()
对以上代码做一下说明。
- class的泛型可以在继承时直接具体化。如Student,对于继承的Person的两个泛型指定了String, Int两个具体类型。如:
class Student: Person
- 也可以在继承时仍然不具体化,这时把Person的泛型,仍然作为泛型使用,如Teacher类中,仍然作为泛型使用,就是需要把Person的两个泛型,再拿到Teacher中。
class Teacher: Person
- 可选属性为泛型时,因为可以不用在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已经不需要再指定返回值类型了,直接有函数参数推倒就可以了。总结,函数中泛型使用时:
- 一般说来,函数中用的泛型尽量在参数中直接指定完毕,如果没有实例参数指定,也可以指定类型,就如上面的A.Type, B.Type
- 函数的泛型和类和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()
泛型的使用非常广泛,此篇为基础使用,尤其是泛型和协议结合使用会更加灵活多变。不对之处,欢迎指正。