文章出处:http://www.cnblogs.com/dingyingsi/p/3761635.html
1.首先在没有泛型之前,一旦把一个对象丢进java集合中,集合就会忘记对象的类型,把所有对象当成Object类型来处理。当程序从集合中取出对象时,就需要进行强制类型转换,这种强制类型转换不仅代码臃肿,而且容易引起ClassCastException异常。
public class ListErr { public static void main(String[] args) { //创建一个只想保存字符串的List集合 List strList = new ArrayList(); strList.add("Struts2权威指南"); strList.add("基于J2EE的Ajax宝典"); strList.add("轻量级J2EE企业应用实战"); //”不小心“把一个Integer对象”丢进"了集合 strList.add(5); for (int i = 0; i < strList.size() ; i++ ) { //因为List里取出的全部是Object,所以必须强制类型转换 //最后一个元素将出现ClassCastException异常 String str = (String)strList.get(i); } } }
2.为了解决这个问题,在1.5前都采用,创建个List对象的方法,但每遇到个集合都要建个对象
class StrList { private List strList = new ArrayList(); //定义StrList的add方法 public boolean add(String ele) { return strList.add(ele); } //重写get方法,将get方法的返回值类型改为String类型 public String get(int index) { return (String)strList.get(index); } public int size() { return strList.size(); } }
public class CheckType { public static void main(String[] args) { //创建一个只想保存字符串的List集合 StrList strList = new StrList(); strList.add("Struts2权威指南"); strList.add("基于J2EE的Ajax宝典"); strList.add("轻量级J2EE企业应用实战"); //下面语句不能把Integer对象“丢进”集合中,将引起编译异常 strList.add(5); System.out.println(strList); for (int i = 0; i < strList.size() ; i++ ) { //因为StrList里元素的类型就是String类型,所以无需强制类型转换 String str = strList.get(i); } } }
3.泛型
有了泛型就方便了,eg public class GenericList { public static void main(String[] args) { //创建一个只想保存字符串的List集合 List<String> strList = new ArrayList<String>(); strList.add("Struts2权威指南"); strList.add("基于J2EE的Ajax宝典"); strList.add("轻量级J2EE企业应用实战"); //下面代码将引起编译错误 strList.add(5); for (int i = 0; i < strList.size() ; i++ ) { //下面代码无需强制类型转换 String str = strList.get(i); } } }
4.当创建带泛型的自定义类时,在定义该类构造器时,构造器名还是和类名一样,不要增加泛型声明。
从泛型类派生子类,接口,父类不能包含类型形参。
下面例子就是错误的。
public class A extends B<T>{},
使用类型通配符“?’”, 它可以匹配任何类型,
public void test(List <?>){}
5.使用时注意:List<String>不是List<Object>子类 ,特殊的泛型(带类型通配符和类型上限),
public class Apple<T extends Number> { T col; public static void main(String[] args) { Apple<Integer> ai = new Apple<Integer>(); Apple<Double> ad = new Apple<Double>(); //下面代码将引起编译异常 //因为String类型传给T形参,但String不是Number的子类型。 Apple<String> as = new Apple<String>(); } }
6.java泛型不支持泛型数组(List<String> aa=new ArrayList<String>[10];),因为java泛型的设计原则是没有unchecked警告就没有ClassCastExcepiton. 我建议大家遇到集合数组等的时候,自己来检验数据的类型
public class test { public static void main(String[] args) { ArrayList[] aa = new ArrayList[10]; List<Integer> li = new ArrayList<Integer>(); li.add(3); ((Object[]) aa)[1] = li; Object target = aa[1].get(0); if (target instanceof Integer) { Integer s = (Integer) target; System.out.println(s); } } } 由于java的泛型只是编译时做下检验,大家不要想的过于强大,他的最大作用只是增强代码的可读性,别的方面也没见多大的作用。
7.什么时候写泛型?有什么好处?
最简单的体现,只要使用到了带有<>的类和接口,就指定具体对象类型。
泛型的好处:
1, 将运行时出现的ClassCastException问题,再编译时期给解决了。运行就安全了。
2, 避免了强制转换的麻烦。
所以泛型就是JDK1.5后出现的一个安全机制。
泛型的理解?
首先在没有泛型之前,一旦把一个对象丢进java集合中,集合就会忘记对象的类型,把所有对象当成Object类型来处理。
当程序从集合中取出对象时,就需要进行强制类型转换,这种强制类型转换不仅代码臃肿,而且容易引起ClassCastException异常。
1,泛型就是传参数。
2,泛型替代了Object。
什么是泛型的擦除和补偿?
泛型是编译时期的安全机制。
编译时,通过泛型机制,编译器多了多元素类型进行检查的步骤。
如果检查通过,产生的class文件时不带有泛型的:也就是泛型的擦除。
泛型的补偿:在对元素存储的时候,可以完成类型的判断。
可是在对元素取出的时候,怎么用指定的类型来接收呢?
JVM运行时,会获取元素的类型,并用该类型对元素进行转换即可。
什么时候使用泛型类?
当类中要操作的引用数据类型不确定的时候,以前使用的是共性类型Object,
现在可以使用泛型来解决。
什么时候使用泛型方法?
当方法操作的引用数据类型不确定的时候,就使用泛型方法。
如果方法是静态的,是无法访问类上定义的泛型的。
如果该方法还需要泛型。
必须将泛型定义在方法上。
泛型的限定。
如果要对操作的类型进行限定,只操作一部分类型时,可以使用泛型的高级功能。
?extends E:可以接收E类型和E的子类型。这叫泛型的上限。
?super E:可以接收E类型或E的父类型。这叫泛型的下限。
什么时候会用? extends E 呢?(往集合中添加集合的时候经常使用)
一般在存储具体引用类型时,使用这种情况。
因为存储E类型或者E类型的子类型,在取出的时候都可以用E类型来操作这些元素。
这时可以保证类型是安全的。
下限什么时候用?
从集合中取出对象进行操作时,可以使用下限。
例如:比较器。无论集合中的元素对象的类型是什么,只要比较器的指定的类型可以接收这些对象完成比较,就可以了。
所以比较器的类型,可以是集合中当前元素的类型,也可以是该元素类型的父类型。
泛型使用的误区:
1, 凡是安全的都特别严格。一定要保证座左右两边一致。
2, 不能操作特有对象。
参数定义的集合类型是一个范围,而接受的实际参数是以一个实体,该实体肯定会指定该范围中的某一个具体类型。而该类型是创建容器时指定的,到底是哪种类型该方法是不确定的,那么就不可以在该方法内,进行具体类型对象的定义和操作。
建议定义泛型时,左右两边一定要一致。如果不一致要保证一点,左边在声明时可以声明一个类型范围,右边在实例化时指定的具体类型必须是左边类型范围中的一种。