泛型
一、为什么要有泛型
1、泛型的设计背景:
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的不放是确定的,李儒关羽这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList这个就是类型参数,即泛型。
2、为什么要有泛型?
那么为什么要有泛型呢,直接Object不是也可以存储数据吗?
1、解决元素存储的安全性问题,好比商品、药品标签,不会弄错。2.解决获取数据元素时,需要类型强制转换的问题,好比不用每回拿商品、药品都要辨别。


2、Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。
二、泛型的使用
1.泛型是jdk 5.0新增的特性
2、在集合中使用泛型:
总结:
①集合接口或集合类在jdk5.0时都修改为带泛型的结构。
②在实例化集合类时,可以指明具体的泛型类型
③指明完以后,在集合或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
比如:add(E e) --->实例化以后:add(Integer e)
④注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换。
⑤如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型
注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
三、自定义泛型类
public class OrderTest<T>{ Map<Integer,T> map; T type; public T getType() { return type; } public void setType(T type) { this.type = type; } public void setType(Integer id, T entity) { map.put(id,entity); } public T getType(Integer id) { return map.get(id); } public void update(Integer id,T entity){ if (map.containsKey(id)){ map.put(id,entity); } } public List<T> list(){ Collection<T> values = map.values(); return new ArrayList<>(values); } public T delete(Integer id){ return map.remove(id); } public <E> List<E> test(E[] arr){ List<E> list = new ArrayList<>(); Collections.addAll(list, arr); return list; } }
自定义泛型方法:
public <E> List<E> test(E[] arr){ List<E> list = new ArrayList<>(); Collections.addAll(list, arr); return list; }
注意点:
1、泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:<E1,E2,E3>
2、泛型类的构造器如下:public GenericClass(){}。而下面是错误的:public GenericClass(){}
3、实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4、泛型不同的引用不能相互赋值。尽管在编译时ArrayList和ArrayList是两种类型,但是,在运行时只有一个ArrayList被加载到JVM中。
5、泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价于Object。
6、经验:泛型要使用一路都用。要不用,一路都不要用。7、如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
8、jdk1.7,泛型的简化操作:ArrayList flist = new ArrayList<>();
9、泛型的指定中不能使用基本数据类型,可以使用包装类替换。
10、在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
11、异常类不能是泛型的
四、泛型在继承上的体现
不能使用new E[]。但是可以: E[] elements = (E[])new Object[capacity];
参者: ArrayList源码中声 明: Object] elementData,而非泛型参数类型数组。
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
- 子类不保留父类的泛型:按需实现
- 没有类型擦除
- 具体类型
- 子类保留父类的泛型:泛型子类
- 全部保留
- 部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
public void test(){ Object obj = new Object(); String str = new String(); obj = str; Object[] objects = null; String[] strings = null; objects = strings; //编译不通过 //Date date = new Date(); //str = date; //不兼容的类型。实际为 java.util.Date',需要 'java.lang.String' List<Object> list1 = null; List<String> list2 = null; //此时的list1和list2的类型不具有字符类关系 //编译不通过 //list1 = list2; /* 反证法: 假设list1 = list2; list1.add(123);导致混入非String的数据。出错 */ show(list1); show1(list2); } public void show1(List<String> list){ } public void show(List<Object> list){ }
虽然类A是类B的父类,但是G<A>和G<B>二者不具备字符类关系,二者是并列关系。
补充:类A是类B的父类,A<G>是B<G>的父类
五、通配符的使用
使用类型
1、通配符:?
比如:List<?> ,Map<?,?>
List<?>是List、List等各种泛型List的父类。
2、读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3、写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
- 唯一的例外是null,它是所有类型的成员。
- 将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList(); c.add(new Object()); // 编译时错误因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
4、另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。
代码演示:
/** * 1.泛型在继承方面的体现 * 2.通配符的使用 */ public class GenericTest { /** * 2.通配符的使用 * 通配符:? * * 类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?> */ @Test public void test3(){ List<Object> list1 = null; List<String> list2 = null; List<?> list = null; list = list1; list = list2; //编译通过 print(list1); print(list2); } public void print(List<?> list){ Iterator<?> iterator = list.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); System.out.println(obj); } } }


浙公网安备 33010602011771号