一、泛型(Generic)在集合中的使用
1.作用
(1)解决元素存储的安全问题
(2)解决获取数据元素时,需要类型强转的问题
2.代码案例
//在集合没有使用泛型的情况下 List list = new ArrayList(); //list用来存放成绩 list.add(60); list.add(70); list.add(80); //在没有使用泛型时,任何Object及其子类都可以添加进来,不安全 list.add(new String("AA")); for(int i = 0; i < list.size();i++){ //在强转字符串“AA”为int型时报ClassCastException的异常 int score = (Integer)list.get(i); System.out.println(score); }
//在集合中使用泛型 List<Integer> list = new ArrayList<Integer>(); //list用来存放成绩 list.add(60); list.add(70); list.add(80); //list.add(new String("AA"));编译时就不通过,只能添加Integer类型的元素 Iterator<Integer> it = list.iterator();//用Iterator也需要声明泛型为Integer while(it.hasNext()){ System.out.println(it.next()); }
二、自定义泛型类、泛型接口、泛型方法
1.特点
(1)当实例化泛型类的对象时,指明泛型的类型,对应的类中所有使用泛型的位置,都变为指定的类型
(2)如果泛型类在实例化时没有指明泛型的类型,默认为Object类
(3)通过对象调用泛型方法时,指明泛型的类型,这个类型可以和泛型类的泛型的类型不一致。
(4)静态方法中不能使用类的泛型(因为类的泛型在实例化才指明类型,所以不能在静态方法中使用)
(5)不能在try-catch中使用泛型
2.代码示例
//自定义泛型类 public class Order<T> {//实例化后,类中的所有T都变为指定的类型 private T t; List<T> list = new ArrayList<>(); public void add(){ list.add(t); } public T getT(){ return t; } public void setT(T t){ this.t = t; } //声明一个泛型方法,实现任意类型的数组到集合的复制 public <E> List<E> fromArrayToList(E[] e,List<E> list){//注意“<E>”的位置,不能少 for(E e1 : e){ list.add(e1); } return list; } } //继承泛型类或泛型接口时,可以指明泛型的类型,也可以不指明 class SubOrder extends Order<Integer>{//class SubOrder<T> extends Order<T>{} } //泛型类的使用 public class TestGeneric{ public static void main(String[] args){ Order<Boolean> order = new Order<Boolean>(); order.setT(true);//参数只能是Boolean类型的 System.out.println(order.getT());//输出true order.add(); List<Boolean> list = order.list; System.out.println(list);//输出[true] SubOrder o1 = new SubOrder(); //只能用List<Integer>类型的List接收SubOrder类型的list。 List<Integer> list1 = o1.list; Integer[] i = new Integer[](1,2,3); List<Integer> list2 = new ArrayList<>(); List<Integer> list3 = order.fromArrayToList(i,list2); System.out.println(list2);//输出[1,2,3] System.out.println(list3);输出[1,2,3] } }
三、泛型与继承的关系以及通配符
1.泛型与继承的关系:即使类A是类B的子类,List<A>也不是List<B>的子接口。
Object obj = null; String str = "AA"; obj = str;//String是Object的子类,这么写没错 Object[] obj1 = null; String[] str1 = new String[]("AA","BB","CC"); obj1 = str1;//也没错 List<Object> list = null; List<String> list1 = new ArrayList<String>(); list = list1;//报错,类型不匹配 //用反证法,假设list = list1正确 //list.add(123);list可以传入Object类型的元素 //String str = list.get(0);//报错,所以假设不正确
2.通配符—— ?
(1)特点
A:List<A>、List<B>. . . . . .都是List<?>的子类
B:? extends A:可以存放A及其子类
C:? super A:可以存放A及其父类
List<?> list = null; List<Object> list1 = new ArrayList<Object>(); List<String> list2 = new ArrayList<String>(); list = list1;//没错,LIst<?>是List<Object>的父类 list = list2;//没错,LIst<?>是List<String>的父类 List<? extends Number> list3 = null; LIst<Integer> list4 = null; list3 = list4;//没错 list3 = list1;//报错,只能是Number或Number的子类 List<? super Number> list5 = null; list5 = list1;//没错
(2)通配符的使用
List<String> list = new ArrayList<String>(); list.add("AA"); list.add("BB"); List<?> list1 = list; //可以读取声明为通配符的集合类的对象 Iterator<?> iterator = list1.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next());//把next()返回的值都看成是Object类型的,所以可以读取 } //不允许向声明为通配符的集合类的对象写入对象,唯一例外的是null list1.add("CC");//报错,不知道写入的是什么类型的对象 list1.add(123);//报错 list1.add(null);//正确