java泛型(generic)
2016-07-11
问题
- 向集合存储元素存在安全性问题,即很可能不知道已有的集合中已经存储的元素的类型是什么,这时就不应该向该集合随便添加元素,否则与集合的设计的初衷相悖,因为集合存储的是同类型的元素,而且以后取集合中的元素不知道该转为什么类型。
- 从集合中获取元素需要强制类型转换,可能出现ClassCastException问题
未使用泛型举例:
说明:任何Object子类均可以存入Object集合,但是从集合中获取元素,则是Object对象,需要强制转换,可能抛异常。
意义
使用泛型能够限定集合中存放的元素类型,标记集合中的元素类型,对存入集合的元素进行类型检查,让集合中的元素类型一致。泛型就是“参数化类型”,类比于形参和实参的关系,泛型就是让原本具体类型进行参数化,在泛型具体化或者泛型调用的时候,将类型实参传给类型形参,例如String这个实参传给T形参。
使用
未使用泛型
1 public void test1(){ 2 //未使用泛型 3 List list=new ArrayList(); 4 list.add(1); 5 //加入类型不一致 6 list.add("one"); 7 for(int i=0;i<list.size();i++){ 8 //可能出现ClassCastException 9 int num=(int) list.get(i); 10 } 11 }
使用泛型
1 public void test2(){ 2 //使用泛型,保证了集合使用的安全性 3 List<Integer> list=new ArrayList<>(); 4 list.add(1); 5 //不能加入其它类型 6 // list.add("one"); 7 for(int i=0;i<list.size();i++){ 8 //不会出现ClassCastException 9 int num=list.get(i); 10 } 11 }
使用细则
自定泛型类、泛型接口、泛型方法
泛型类及方法的使用
1 class Animal<T>{ 2 3 } 4 5 //Cat继承Animal 6 //指定T为Short,Cat为具体类,不是泛型类 7 class Cat extends Animal<Short>{ 8 } 9 10 //泛型类Persion继承Animal 11 //还未指定T,Persion是泛型类 12 class Persion<T> extends Animal<T>{ 13 private T t; 14 List<T> list=new ArrayList<>(); 15 public T getT(){ 16 return this.t; 17 } 18 public void setT(T t){ 19 this.t=t; 20 } 21 //泛型方法 22 public <E> E change(E e){ 23 return e; 24 } 25 } 26 27 public class Generic_Use{ 28 public static void main(String[] args) { 29 //泛型类使用 30 { 31 //在实例化泛型类时,未指定泛型的具体类型,则泛型的默认类型为Object 32 Persion persion=new Persion(); 33 persion.setT("helloWorld"); 34 //persion.getT() 返回Object类型 需要强制转换为String 35 String str=(String) persion.getT(); 36 } 37 { 38 //在实例化泛型类时,指定了泛型的具体类型A,则所有出现泛型的地方均为类型A 39 Persion<String> persion=new Persion<>(); 40 persion.setT("helloWorld"); 41 //persion.getT() 返回String类型 42 System.out.println(persion.getT()); 43 { 44 //泛型方法的具体化 45 //persion.change(11)均返回Integer类型 46 System.out.println(persion.change(11)); 47 //通用的泛型方法具体化 48 System.out.println(persion.<Integer>change(11)); 49 //出错 无法指定类泛型具体类型为Integer的方法又用String进行 具体化 50 // System.out.println(persion.<Integer>change("12")); 51 } 52 } 53 }
泛型接口的使用类似泛型类,可类比使用
泛型与继承
1 public class Generic_extends { 2 public static void main(String[] args) { 3 { 4 //普通类与继承 5 Object object=null; 6 String str="str"; 7 object=str; 8 9 Object[] objects=null; 10 String[] strs=new String[]{"str1","str2"}; 11 objects=strs; 12 } 13 14 { 15 //泛型类与继承 16 List<Object> list=null; 17 List<String> list2=null; 18 //出错 List<String> 不是List<Object>的父类 19 // list=list2; 20 { 21 //证明 22 //反证:若List<Object>是 List<String>的父类 23 list.add(11); 24 String str=list2.get(0);//会出错 25 26 //结论:若A是B的父类,但是List<A>不是List<B>的父类 27 } 28 } 29 } 30 }
泛型的通配符
原则:对于集合的使用,则是严进宽出,即放入集合要类型准确一致,取出可以模糊取出
注意点:静态方法和catch块不能使用泛型
建议:参考C++中的模板编程思想
1 public void test3(){ 2 //通配符? 3 { 4 //List<?>是所有List<对象> 的父类 5 //List<?>可以指向任意的List<对象>实例对象,并取出其中的元素,但是不能放入入元素 6 7 List<?> list=new ArrayList<>(); 8 List<Object> list2=new ArrayList<>(); 9 List<String> list3=new ArrayList<>(); 10 list2.add(12); 11 list3.add("bbb"); 12 list=list2; 13 list=list3; 14 15 { 16 //list取出存在list3的元素 17 Iterator<?> iterator=list.iterator(); 18 while(iterator.hasNext()){ 19 System.out.println(iterator.next()); 20 } 21 22 //list不能直接通过自己向他的子类放入元素 23 //list向list3中存入元素 24 // list.add("ccc");//出错 25 26 //特例 空对象 27 list.add(null); 28 29 30 } 31 } 32 33 { 34 //边界通配符 ? extends A 35 //允许集合中的元素类型是A及其子类(类型通配符上限 类比小于号《) 36 37 List<? extends Number> list4=new ArrayList<>(); 38 List<Integer> list5=new ArrayList<>(); 39 list5.add(12); 40 list4=list5; 41 //list4取出存在list5中的元素 42 System.out.println(list4.get(0)); 43 //list4向list5中存入元素 44 // list4.add(12);//出错 45 } 46 47 { 48 //边界通配符 ? super A 49 //允许集合中的元素类型是A及其父类(类型通配符下限 类比大于号》) 50 51 List<? super Integer> list6=new ArrayList<>(); 52 List<Integer> list7=new ArrayList<>(); 53 list7.add(12); 54 55 list6=list7; 56 //list6取出存在list7中的元素 57 System.out.println(list6.get(0)); 58 //list6向list7中存入元素 59 // list6.add(12);//出错 60 61 // list7=list6;//出错 62 } 63 64 //结论: 65 //对于泛型,父类可以从子类中取元素,但是不能存元素到子类中 66 //或者说可以从通配符泛型类中取出元素,但是不能写入元素 67 //体现了,集合中的元素严进宽出,写入集合的元素需要具体类型,而读取集合的元素没有要求 68 } 69 }
泛型提高
1 public void test4(){ 2 List<Integer> list=new ArrayList<>(); 3 List<String> list2=new ArrayList<>(); 4 5 System.out.println("list的类型="+list.getClass()); 6 System.out.println("list2的类型="+list2.getClass()); 7 //结果: 8 //list的类型=class java.util.ArrayList 9 //list2的类型=class java.util.ArrayList 10 11 }
从上面程序可以看出方法test4中list和list2的实际运行类型是一致的。
这关系到泛型的类型擦除。
原因分析:泛型设计的初衷就是在程序编写的时候就能够进行类型检查,避免程序运行时出错。对于泛型类型检查通过后,所以在编译时,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。其实就是好比C++中模板编程,所有同一个泛型的具体化对象实际均是同一类型,所有同一泛型的具体化对象其实是共享同一份代码,即母代码,可以类比java中类和实例之间的关系学习。
时间: 2024-11-08 18:19:29