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 - 逆變性,父類型可以強轉到子類型