一.为什么使用泛型
1.之前没使用的情况
在泛型出现之前,一旦把一个对象丢进集合中,集合就会忘记对象的类型,把所有的对象当成object类型来进行处理。当程序从集合中取出对象后,就需要进行强制类型转换,这会让代码变得臃肿,同时类型转换的时候也有可能会出现,ClassCastException异常
ArrayList collection1 = new ArrayList();
collection1.add(1);
collection1.add(1L);
collection1.add("abc");
//int i = (Integer)collection1.get(1);
2.使用后
很大程度上是为了让集合记住其中元素的数据类型类型,使用了泛型之后,编译器在编译代码的时候,会进行类型检查,如果试图向集合中添加不满足类型要求的对象,编译器会提示类型错误,这提高了代码的安全性;其次使用泛型,可以提高代码的通用性
ArrayList<String> collection2 = new ArrayList<String>();
//collection2.add(1);
//collection2.add(1L);
collection2.add("abc");
String element = collection2.get(0);
二.介绍
本质:
1.泛型是给编译器看的,让编译器挡住非法类型的输入,编译器编译带类型说明的集合,生成字节码前会去除掉“类型”信息,使程序的运行效率不受影响
2.对于参数化的泛型类型,getClass()方法的返回值跟原始类型相同,
ArrayList<Integer> collection3 = new ArrayList<Integer>();
System.out.println(collection3.getClass() == collection2.getClass());//true
//collection3.add("abc");
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
由于编译生成的字节码会去除掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型的集合中加入另一个类型的数据。
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");//通过反射将String类型的数据加入到了int集合中
三.泛型中的通配符
使用?可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用参数无关的方法,不能调用参数化有关的方法
public static void printCollection(Collection<Object> c){
for(Object obj :c){
System.out.println(obj);
}
c.add("abc");//正确
c = new ArrayList<String>();//错误
}
public static void printCollection(Collection<?> c){
for(Object obj :c){
System.out.println(obj);
}
c.size();//正确
c.add("abc");//错误
c = new ArrayList<String>();//正确
}
类型通配符的扩展
ArrayList<? extends Number>
ArrayList<? super Integer>
HashMap<String,Integer> maps = new HashMap<String, Integer>(); //泛型参数化,不能是基本类型
maps.put("abc", 28); //此处的28被自动装箱成Integer类型,
maps.put("bcd", 35);
maps.put("cdf", 33);
Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
for(Map.Entry<String, Integer> entry : entrySet){
System.out.println(entry.getKey() + ":" + entry.getValue());
}
四.自定义泛型
1.定义泛型方法
private static <T> T add(T x,T y){
return null;
}
add(3,4); T —Integer
add(3,3.5); T —Number
add(3,”abc”); T —Object
T只能是引用类型,不能是基本类型,上面的基本类型被自动装箱成相应的引用类型
类型限定符的扩展
ArrayList //即使是接口 ,也用extends而不是implements
ArrayList
2.定义泛型类型
如果类中有多个方法使用泛型,使用类级别的泛型
public class GenericDao<E> {
public void add(E x){
}
public E findById(int id){
return null;
}
public void delete(E obj){
}
public void delete(int id){
}
public void update(E obj){
}
泛型类,可以看做普通类的工厂类
当一个变量被声明为泛型时,只能被实例变量跟实例方法调用(也可以被内嵌类型调用),不能被静态变量跟静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有声明为泛型的类型参数
//此处错误
public static void update2(E obj){
}
//此处正确
public static void update2(E obj){
}