泛型:被不同类型的对象所重用
用继承(objec)实现的两个问题:
1 获取一个值时必须强制进行类型转换
2 可以向数组列表中添加任何类的对象
类型参数:(可读性、安全性)
定义简单泛型类:
public class Pari<T>{}
public class Pair<T,U>{}
public class HashMap<K,V>{}
使用泛型类:
ArrayList<String> files = new ArryList<String>()(两边必须相同)
ArrayList<String> files = new ArryList()
ArrayList files = new ArryList<String>()
使用一个泛型类时,只要在后面加上<具体的类型>
定义泛型方法(可以在普通类及泛型类中定义):
class ArrayAlg{
public static <T> T getMiddle(T...a){ //注意类型变量的位置
return a[a.length/2]
}
}
泛型方法的调用:
String middle=ArrayAlg.<String>getMiddle("bb","cc","dd")
String middle=ArrayAlg.getMiddle("bb","cc","dd")
类型变量的限定:<T extends BoundingType>这里的T和BoundingType既可以是类也可以是接口,使用extends更加表现出子类的概念
<T extends BoundingTypeA & BoundingTypeB>可以多个超接口,最多一个类(必须第一个)
静态方法不能使用类定义的泛形,而应单独定义泛形。 泛形的典型应用:BaseDao和反射泛型
泛型与虚拟机:
虚拟机没有泛型类型对象
泛型代码与虚拟机:
自动提供原始类型,擦除类型变量,替换为限定类型(无则用object)
当调用泛型方法时,编译器对返回的object进行强制类型转换
泛型类擦除造成原本在泛型类子类中覆盖的方法变成了重载。(参数类型变成了object)导致子类出现了同一方法名两种参数的方法,调用时本来只希望调用子类的方法(多态),结果有可能调用了泛型超类中的参数为objec的方法。编译器自动在子类中加入桥方法:
public void setSecond(object a){setSecond((Date) a)}
同样在有返回类型的方法中,也会产生两个相同参数类型的方法。虚拟机根据参数类型和返回类型确定一个方法。
一个方法覆盖另一个方法时可以指定一个更加严格的返回类型。
1 虚拟机中没有泛型,只有普通类和方法;2 所有类型参数都用他们的限定类型替换 3 桥方法保持多态 4为保持安全性,必要时插入强制类型转换
允许泛型代码和遗留代码之间能够互操作。
泛型的约束与局限(大多由擦除引起):
1.不能用基本类型实例化类型参数,objec不能储存double的值。
2.运行时的类型查询只适用于原始类型。使用instanceof,getcalss或泛型类型的强制转化时,都是类型擦除后的效果。
3.不能创建参数化类型的数组:Pair<String>[] table=new Pair<String>[10]//error,擦除机制将会导致table数组中可以插入任意的Pair<otherclass>
4.Varargs警告,向参数可变的方法传递一个泛型类型的实例。由于个数可变参数实际上是一个数组,所以会违反第三条,但是只会得到一条警告。
5.不能实例化类型变量:(如果想通过T来构造对象)
new T(),T.class//both error
public Pair(){first=new T();second=new T();}//error
saddly,由于不能调用T.class:
first=T.class.newInstance()也是错误的
如果非要通过反射调用Class.newInstance来构造泛型对象:在Pair中新建一个泛型方法,由于Class本身是泛型,String.class是Class<String>的一个实例
public static <T> Pair<T> makePair(Class<T> cl){ try{return new Pair<>(cl.newInstance(), cl.newInstance()) ;} catch(Exception ex){return null;} }
调用:
Pair<String> p = Pair.makePair(string.class);
不能构造一个类型变量的数组:
public static <T> T[] minmax(T[] a){T[] mm = new T[2];....}//error
6. 泛型类的静态上下文中类型变量无效
7. 不能抛出或捕获泛型类的实例:
泛型类cannot extend throwable;不能catch类型变量(声明中可以throws类型变量)
。。。。
泛型类型的继承规则:
Pair<employee>和Pair<manager>之间没有关系
Pair<employee> b=Pair<manager> a //error
假设可以转换,则会出现可以在b中存储低级别雇员(由于没有保护)
注意数组和泛型的区别,如果一个manager[]数组赋值给employee[]变量a,则这个变量会带有特别的保护,即低级别雇员无法存储到a[0]。
Pair<Employee>时原始类型Pair的一个子类型
泛型类可以扩展或实现其他的泛型类。如ArrayList<T>实现了List<T>接口。
通配符类型:
public static void printBuddies(Pair<employee> p){....}
由于Pair<employee>和Pair<manager>之间没有关系,所以上述方法中不能传入Pair<manager>
引入通配符:public static void printBuddies(Pair<? extends employee> p){....}
Pair<manager>是Pair<? extends employee>的子类型