## 泛型
### 基本概念
很多時候我們關心的不是數據類型,而是能力,針對接口和能力編程,不僅可以復用代碼,還可以降低耦合,提高靈活性。
`泛型`的字面意思就是**廣泛的類型**;類、接口和方法代碼可以應用于非常廣泛的類型,代碼與它們能夠操作的數據類型不再綁定在一起,同一套代碼可以用于多種數據類型,這樣,不僅可以復用代碼,降低耦合,而且可以提高代碼的可讀性和安全性。
### 泛型基本概念
```
class Pair<Z, V>{
Z first;
V second;
public Pair(Z first, V second) {
this.first = first;
this.second = second;
}
public Z getFirst() {
return first;
}
public V getSecond() {
return second;
}
}
```
- 類名后面多了```<Z> <V>```;
- ```<Z> <V>```是什么呢?```<Z> <V>```表示類型參數,泛型就是類型參數化,處理的數據類型不是固定的,而是可以作為參數傳入。怎么用這個泛型類,并傳遞類型參數呢?
```
Pair<Integer, String> pair = new Pair<Integer, String>(111, "222");
System.out.println(pair.getFirst() + " " + pair.getSecond());
Pair<Double, Float> pairStr = new Pair<Double, Float>(1.2, 3.2f);
System.out.println(pairStr.getFirst() + " " + pairStr.getSecond());
```
### 基本原理
泛型類型參數到底是什么呢?為什么一定要定義類型參數呢?定義普通類,直接使用Object不就行了嗎?比如,Pair類可以寫為:
```
class Pair {
Object first;
Object second;
public Pair(Object first, Object second) {
this.first = first;
this.second = second;
}
public Object getFirst() {
return first;
}
public Object getSecond() {
return second;
}
}
Pair pair = new Pair(111, "aaa");
System.out.println(pair.getFirst() + " " + pair.getSecond());
Pair pair2 = new Pair(1.2, 3.2f);
System.out.println(pair2.getFirst() + " " + pair2.getSecond());
```
這樣是可以的。實際上,Java泛型的內部原理就是這樣的。
> Java有Java編譯器和Java虛擬機,編譯器將Java源代碼轉換為.class文件,虛擬機加載并運行.class文件。對于泛型類,Java編譯器會將泛型代碼轉換為普通的非泛型代碼,就像上面的普通Pair類代碼及其使用代碼一樣,將類型參數T擦除,替換為Object,插入必要的強制類型轉換。Java虛擬機實際執行的時候,它是不知道泛型這回事的,只知道普通的類及代碼。Java泛型是通過擦除實現的,類定義中的類型參數如`<T>`會被替換為Object,在程序運行過程中,不知道泛型的實際類型參數,比如`Pair<Integer>`,運行中只知道Pair,而不知道Integer。認識到這一點是非常重要的,它有助于我們理解Java泛型的很多限制。
### 泛型的好處
* 類型安全
```
// 把運行時異常轉換為編譯時異常
Pair<Integer, String> pair3 = new Pair<Integer, String>(111, 222);
```
* 消除強制類型轉換
### 小栗子
```
// 自定義數組
class DynamicArray<T> {
// 默認的一個長度
private static final int DEFAULT_CAPACITY = 10;
// 初始化size 默認為0
private int size;
// 數組
private Object[] elementData;
// 構造方法
public DynamicArray() {
elementData = new Object[DEFAULT_CAPACITY];
}
// 需要長大
private void grow(int minCapacity) {
int oldSize = elementData.length;
if (oldSize >= minCapacity) {
System.out.println("沒有達到擴充數組的地步 " + minCapacity);
return;
}
int newCapacity = oldSize * 2;
System.out.println("擴充后的長度 " + newCapacity);
// 擴充
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 添加方法
public Boolean add(T t) {
grow(size + 1);
elementData[size] = t;
size = size + 1;
return true;
}
// 獲取元素
public T get(int index) {
return (T)elementData[index];
}
// 獲取size
public int size() {
return size;
}
}
// 調用
// 支持各種類型 Doublue Integer new Pair<String, Integer>()
// Double
DynamicArray<Double> arr = new DynamicArray<Double>();
Random random = new Random();
int size = 1+ random.nextInt(100);
for (int i = 0; i < size; i++) {
arr.add(Math.random());
}
Double d = arr.get(random.nextInt(size));
System.out.println(d + " / " + arr.size());
// Integer
DynamicArray<Integer> arr2 = new DynamicArray<Integer>();
arr2.add(12);
System.out.println("\n" + arr2.get(0));
// 自定義對象
DynamicArray<Pair<String, Integer>> arr3 = new DynamicArray<Pair<String, Integer>>();
Pair<String, Integer> pair = new Pair<String, Integer>("aaa", 23);
arr3.add(pair);
System.out.println("\n" + arr3.get(0).toString());
```
這就是一個簡單的容器類,適用于各種數據類型。