## 泛型
### 基本概念
很多时候我们关心的不是数据类型,而是能力,针对接口和能力编程,不仅可以复用代码,还可以降低耦合,提高灵活性。
`泛型`的字面意思就是**广泛的类型**;类、接口和方法代码可以应用于非常广泛的类型,代码与它们能够操作的数据类型不再绑定在一起,同一套代码可以用于多种数据类型,这样,不仅可以复用代码,降低耦合,而且可以提高代码的可读性和安全性。
### 泛型基本概念
```
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());
```
这就是一个简单的容器类,适用于各种数据类型。