一、为什么要有泛型(jdk1.5之后新增的特性)
泛型:就是在定义一个类或者接口时,可以通过一个标识表示属性、方法的参数或者方法的返回值的类型,这个类型参数在被使用的时候(比如继承类、实现接口,或者用类型声明变量,创建对象时)确定,(即传入类型实参)
为什么要有泛型:
1、元素存储时,编译期间没有类型检查,可能会出现安全问题。
2、获取元素时,类型强制转换容易出问题。
使用泛型后:
1、编译期间就会进行类型检查,保证数据安全
2、避免了类型强制转换
二、在集合中使用泛型
1、集合接口或者集合类在jdk5.0之后,都修改为带泛型的结构(jdk7之后一个新特性,类型推断,前面变量声明里面指明了类型,后面构造器里面不再需要指明)
2、实例化集合类时,可指明泛型的类型
3、指明泛型类型之后,内部使用到这个泛型的位置,都指定为这个泛型的类型
4、泛型的类型,必须指定为类,不能是基本数据类型
5、实例化集合时如果没有指定泛型类型,默认是Object类型
三、自定义泛型结构
3.1、泛型类,泛型接口:
1、实例化过程中如果没有指明,则认为是Object类型
2、子类在继承父类时如果指明了泛型类型,子类在实例化时则不再需要指明
注意事项:
1、泛型类可以有多个参数,一起放在<E1,E2,E3>中
2、泛型不同的引用不能相互赋值(不过ArrayList<Integer>, ArrayList<String>,在编译时是不同的类,运行时只有一个ArrayList记载到JVM中)
3、泛型类的构造器
public GenericClass(){} //是正确的 public GenericClass<E>(){} //是错误的
4、泛型类如果不指定,将被擦除,对应的类型按Object处理,但不等价于Object,
经验:泛型要么一路都使用,要么一路都不使用
5、静态方法和静态属性都不能使用类的泛型,因为类的泛型在创建对象才能被确定。
6、泛型类与异常:首先泛型类不能继承异常类
在try catch中的用法;
class Student<T extends Exception>{
public void test(T a){
try {
throw a; //此处不报错
}catch (T e){ // 此处报错,无论是否指定T的上限,都不能在此处使用
}
}
}
7、不能使用new T[]。但是可以:T[] elements = (T[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
8、父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自
己的泛型
3.2、泛型方法:
方法中出现了泛型结构,就叫泛型方法,并且这个参数和类的泛型参数没有任何关系。泛型方法所属的类
是不是泛型类没有任何关系
泛型方法可以声明为静态的,因为泛型方法中的参数类型,是调用方法时确定的,不是实例化对象时确定的
四、泛型在继承上的体现
假如类A是类B的父类,G<A>和G<B>不具有父子类的关系,二者是并列关系;
补充:但是类A<G>是类B<G>的父类
五、通配符的使用:
5.1、通配符的出现:
上面我们知道 G<A>和G<B>不具有父子类的关系,是并列关系,但是他们有个共同的父类G<?>,这个?就是通配符;
5.2、通配符添加读取数据:
List<?> list = new ArrayList();//此时不能向list里面写数据,除了添加null以外
list.add(null); //---------------------
//允许读取,读取的类型是Object的
List<?> list = new ArrayList();
List<String> list1 = new ArrayList<>();
list1.add("hello");
list = list1;
Object o = list.get(0);
5.3、有限制条件的通配符使用:
List <? extends Student> 指明list的上限,不能添加数据,可以读取数据,读取出来的类型是Student
List<? super Student> 指明list的下限 ,可以添加数据,Student及其子类,也可以读取数据,但是类型不能确定,只能指成Object;