# 第八章 泛型程序设计
## 8.1 为什么使用泛型设计
泛型程序设计意味着编写的代码可以对多种不同类型的对象重用。例如ArrsyList收集任何类的对象
### 8.1.1 类型参数的好处
在Java添加泛型类之前,泛型程序设计是用继承实现的,例如ArrayList只维护一个Objects引用的数组。\
这使得获取一个值必须进行强制类型转换,也没有错误检查。\
```
ArrayList<String> tmp=new ArrayList<>();//类型参数的使用,避免了以上问题
``` 
## 8.2 定义简单泛型类
```
public class Pair<T>
{
    private T first;
    private T second;
    public Pair(){
        first=null;
        second=null;
    }
    public Pair(T first,T second)
    {
        this.first=first;
        this.second=second;
    }
    public T getFirst()
    {
        return first;
    }
    public T getSecond()
    {
        return second;
    }
    public void setFirst(T newValue)
    {
        first=newValue;
    }
    public void setSecond(T newValue)
    {
        second=newValue;
    }
}
```
## 8.3 泛型方法
```
class ArrayAlg
{
    public static <T> T getMiddle(T... a)
    {
        return a[a.length/2];
    } 
}
```
泛型方法可以在普通类也可以在泛型类中定义,调用方法如下
```
String Middle=ArrayAlg.<String>Middle("John","Peter","Jack");
```
## 8.4 类型变量的限定
类或方法有时需要对类型变量加以约束,下面是个例子
```
class ArrayAlg
{
    public static <T extends Comparable> T min(T[] a)
    {
        if(a==0||a.length) return null;
        T smallest=a[0];
        for(int i=1;i<a.length;i++)
        {
            if(smallest.compareTo(a[i])>0) smalleat=a[i];
        }
        return smallest;
    }
}
```
一个类型变量或通配符可以有多个限定
```
T extends Comparable & Serializable
```
## 8.5 泛型代码和虚拟机
### 8.5.1 类型擦除
无论如何定义一个泛型类型,都会有一个原始类型,即去掉类型参数的泛型类型名,类型参数会被替换为限定类型(无限定则为Object)
### 8.5.3 转换泛型方法
```
var interval=new DateInterval(...);
Pair<LocalDate> pair=Interval;//assignment to superclass
pair.setSecond(aDate);
```
类型擦除与多态发生冲突,编译器自动生成桥方法。变量
```
public void setSecond(Object second)
{
    setSecond((LocalDate) second);
}
```
桥方法还可以用于方法重写
```
public class Employee implements Cloneable
{
    public Employee clone() throws CloneNotSupportedException{...}
}
```
Object.clone和Employee.clone被称为有协变的返回类型
实际上Employee类有两个克隆方法:\
Employee clone()\
Object clone()\
合成的桥方法会调用新定义的方法。

总之对于java泛型的转换,有以下的事实:
1. 虚拟机中没有泛型,只有普通的类和方法。
2. 所有的类型参数都会替换为他们的限定类型
3. 会合成桥方法来保持多态
4. 为保持类型安全性,必要时会插入强制类型转换。
## 8.6 限制与局限性
1. 不能用基本类型实例化类型参数,需要使用Integer,Double,...
2. 运行时类型查询只适用于原始类型
```
Pair<String> stringPair=...;
Pair<Employee> employeePair=...;
if(stringPair.getClass()==employeePair.getClass())//they are equal 两次getClass()都会调用Pair.class
```
3. 不能创建参数化类型的数组
```
var table=new Pair<String>[10];//Error
ArrayList:ArrayList<Pair<String>>//更推荐
```
4. 不能实例化类型变量
下面的Pair```<T> ```构造器是非法的
```
public Pair(){ first=new T();second=new T();}//ERROR
```
类型擦除将使得T变成Object,解决方法如下
```
Pair<String> p=Pair.makePair(String::new);
```
5. 泛型类的静态上下文中类型变量无效:类型擦除后只剩下原始类型,禁止使用带有类型变量的静态字段和方法。
6. 不能抛出或捕获泛型类的实例
7. 无论S和T有什么关系,```Pair<S>和Pair<T>```都没有关系
## 8.8通配符类型
```
Pair<? extends Employee>
```
表示任何类型参数是Employee子类的Pair泛型,如```Pair<Manager>,而不是Pair<String>```

通配符超类型限定
```
Pair<? super Manager>//<Employee>和<Object>皆可
```
带有超类型限定的通配符允许你写入一个泛型对象,而带有子类型限定的通配符允许你读取一个泛型对象