1. 泛型:限制类型
-
1.1.泛型使用场景:
-
1.在集合(数组NSArray、字典NSDictionary、集合NSSet)中使用泛型比较常见。
-
2.当声明一个类,但是类里面的某些属性的类型不确定的时候,我们才使用泛型。
-
- 1.2.泛型书写规范
在类型后面定义泛型:NSMutableArray<UITouch *> dataArray
- 1.3.泛型修饰
只能修饰方法的调用。
- 1.4.泛型好处:
1.提高开发的规范,减少程序员之间的交流。
2.通过集合取出来的对象,可以直接当做泛型对象使用。这样我们就可以直接使用.点语法。
2.代码使用泛型:
-
2.1.声明一个泛型为NSString的数组
具体做法就是在 NSMutableArray 后带一个 <NSString *> ,尖括号内部即为泛型类型@property (nonatomic, strong, nullable) NSMutableArray<NSString *> *dataArray;
- 2.2.当我们要给数组添加对象或取出对象的时候,系统就会自动提示应该传入或者取出来的对象的类型,这个类型就是你刚才声明的泛型类型
- 没有使用泛型
[self.dataArray addObject:<#(nonnull id)#>];
- 使用泛型
[self.dataArray addObject:<#(nonnull NSString *)#>];
-
添加不正确的类型,会出现警告
[self.dataArray addObject:@1];
-
我们可以直接将集合中取出来的对象当做泛型使用
NSInteger length = [self.dataArray.firstObject length];
- 代码如下
- 没有使用泛型
3.泛型的自定义
刚才我们只是实现了系统类NSMutableArray的泛型。接下来我们要考虑下,我们怎么样在我们自己的类中声明一个泛型的属性呢?
为了这个目的,我们创建一个 Language 的类表示 “语言”。并且创建两个 Language 的子类,分别叫 Java 和 IOS 。很明显这两个是“某一个类型的语言”。我们创建一个Person的类,给类声明一个泛型,在类的 .h 文件中声明一个声明一个属性,这个属性表示这个人会的语言,即为 IOS 或者 Java 。那么我们有以下两种声明方式:
- id:任何对象都能传进来
@property (nonatomic, strong, null_unspecified) id language;
- Language:在外面调用的时候不能提示具体是哪种语言
@property (nonatomic, strong, null_unspecified) Language *language;
- 代码示例:
#import "Language.h" NS_ASSUME_NONNULL_BEGIN @interface Person : Language /**! 第一种方式*/ @property (nonatomic,strong,null_unspecified) id languageOne; /**! 第二种方式*/ @property (nonatomic,strong,null_unspecified) Language *languageTwo; @end NS_ASSUME_NONNULL_END
因为 Language 这个语言并不能代表这个 Person 究竟会什么语言,我们需要的属性时 IOS 和 Java。这两种都可以在调用的时候传入 Java 和 IOS 两种对象,但它们的缺点也非常明显,若使用 id 则我们可以传入任何对象,而不只是 Java 和 IOS ;使用 Language * 呢,我们没有办法在编译的时候确定这个人究竟会什么语言,而只能在运行时判断。有没有办法让我们在编译的时候就能知道 Person 具体会哪种 Language 呢?
办法就是泛型。
声明自定义类的泛型,我们需要做这样一步:
- (给类)声明一个泛型
@interface JTPerson<ObjectType> : NSObject
看出区别了吗?我们在 interface 类名之后加了一对尖括号 <> ,中间是 ObjectType 。这个就代表泛型。这样我们在声明属性的时候就可以这么写:
@property (nonatomic, strong, null_unspecified) ObjectType language;
也就是,我们现在不指定具体的类型,而在实例化这个类的时候确定这个泛型。若不确定,那么所有的 ObjectType 会自动变成 id 。
像这样:// iOS JTPerson<IOS *> *iOSP = [[JTPerson alloc] init]; // [iOSP setLanguage:@"123"]; // [iOSP setLanguage:<#(IOS * _Null_unspecified)#>]; // [iOSP setLanguage:[[Java alloc] init]];
// Java JTPerson<Java *> *javaP = [[JTPerson alloc] init]; // [javaP setLanguage:@"123"]; // [javaP setLanguage:<#(Java * _Null_unspecified)#>]; // [javaP setLanguage:[[IOS alloc] init]];
这样,我们在声明 Person这个类的时候,就顺便声明了这个类的泛型。这样系统就会在你使用到泛型的属性与方法的时候,自动提醒你声明的泛型类型了。
4. 泛型的协变与逆变
下图是系统 NSArray 的头文件部分,可以看到它使用了自定义泛型并命名为 OBjectType,
在自定义泛型前加了一个 __covariant 的修饰符,这个修饰符就表示协变性
- __covariant - 协变性,子类型可以强转到父类型(里氏替换原则)
- __contravariant - 逆变性,父类型可以强转到子类型