前言
泛型可能是JDK1.5出现的新特性中,最为难以掌握和运用的。本文在掌握泛型的基本应用的基础上,对泛型进行一个探索和思考,会存在诸多不足的地方。
在学习泛型并深入了解泛型之前,我们一定要清楚的将编译时期和运行时期分开。这样才不至于弄混淆。由于时间原因泛型还没完全看完,给出一部分我个
人已经看懂。这其中参看很多资料。在最后会给出参考资料的出处。
1,泛型入门
Java的集合有个缺点:当我们把一个对象(实际是对象的引用,在文章后面都会用对象代替对象的引用)“丢进”集合里,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类(但是其运行时类型是不会变的)
在没有泛型之前,java对集合这样处理,是因为当初集合的设计者并不知道今天的使用者会把什么类型对象放入集合中,既然Object类是所有类的父类,根据多态的特性,让集合中的所有的对象都是Object类型,这样就可以使集合保持任意类型的对象,这样做的好处是具有很好的通用性。
但是,由此而来也有两个问题:
(1)集合对元素类型没有任何限制,当你创建一个只想保存Dog类型对象的集合时,程序员也可以把Cat对象轻易的放进集合对象。(可能程序员不是故意的,但是这样做真的很不安全)。
(2)由于要保证集合的通用性,放进集合中的对象都会丢掉类型信息,都成了Object类型。放进去容易,取出来就变麻烦了,取出的元素通常要进行强制类型转换,还原成它原来的类型。这种强制转换不仅增加了编程的复杂度,也不可避免的带来类型转换异常的问题。
下面的程序是这样的:当我们向一个集合中添加String类型的对象时,不小心添加了一个Integer类型的对象,最后导致类型转换异常:
import java.util.*;
public class GinericTest
{
public static void main(String args[]){
List strList = new ArrayList();
//向集合中添加String对象
strList.add(new String("Class"));
strList.add(new String("Cast"));
strList.add(new String("Exception"));
//由于各种原因,不小心添加了一个Integer对象
strList.add(new Integer(1));
//现在我想取出集合中的元素
for(int i=0;i<strList.size();i++){
String str = (String)strList.get(i);//16行
}
}
}
---------- 运行java程序 ----------
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at GinericTest.main(GinericTest.java:16)
在编译阶段,你可以将集合中的任意对象强制转换成你想要的对象类型,这在语法上是过的去的,但是在运行阶段,很遗憾的时,很容易ClassCastException(类型转换异常)。在没有泛型之前,如果我们向完成这样一件事:这个集合只能存放String类,在编译阶段就让问题暴露出来。
通过类的复用可以完成这样一件事。看看下面的例子:
import java.util.ArrayList;
import java.util.List;
public class GinericTest2
{
public static void main(String args[]){
StrList myList = new StrList();
myList.myAdd(new String("Class"));
myList.myAdd(new String("Cast"));
myList.myAdd(new String("Exception"));
/*myList.myAdd(new Integer(1));
在编译阶段就会将Integer对象挡在集合外面:
GinericTest2.java:11: 无法将 StrList 中的 myAdd(java.lang.String) 应用于 (java.lang.Integer)
*/
for(int i=0;i<myList.mySize();i++){
String s = myList.myGet(i);//类型转换也省去了
System.out.println(s.length());//打印下每个字符串中字符个数
}
}
}
/*通过复用ArrayList类,来完成自己的StrList类
在编译阶段就能保证存入集合的只能是String对象。
*/
class StrList
{
private List strList = new ArrayList();
//定义自己的add()方法
public void myAdd(String str){
strList.add(str);
}
public String myGet(int i){
return (String)strList.get(i);
}
public int mySize(){
return strList.size();
}
}
---------- 运行java程序 ----------
5
4
9
输出完成 (耗时 0 秒) - 正常终止
关于类的复用:当你只需要一个类的部分功能时,你就可以是有复用而不是继承。
当然上面的例子是成功的挡住了其它类型的对象。但是新的问题又来了,现在我要有一个集合用来专门存放Dog类,还要一个集合存放Cat类等等。我们不得不为每一个类再写一遍复用类。这样编程也变得很麻烦
泛型的出现很好的解决了这个问题。要求是同样的:定义一个ArrayList集合,用来存放String类型的对象。看泛型是如何实现的
import java.util.ArrayList;
import java.util.List;
public class GinericTest3
{
public static void main(String args[]){
List<String> strList = new ArrayList<String>();//尽量面向接口编程
strList.add(new String("Class"));
strList.add(new String("Cast"));
strList.add(new String("Exception"));
/*strList.add(new Integer(1));
手一抖,又想把一个Integer对象放入集合中。编译后的提示:
---------- 编译java程序 ----------
GinericTest3.java:15: 找不到符号
符号: 方法 add(java.lang.Integer)
位置: 接口 java.util.List<java.lang.String>
strList.add(new Integer(1));
^
1 错误
输出完成 (耗时 0 秒) - 正常终止
*/
}
}
我们看看出错提示,很有意思:在接口java.util.List<java.lang.String>找不到add(java.lang.Integer)这个方法。
这个出错提示我将在下一章节给出答案。