对于熟悉JAVA语言的coder来说,泛型绝对曾让自己伤透脑筋,因为java中的泛型就像是一个糖果,但嚼起来却痛苦不堪(可能有点过分,不过看很多论坛贴吧的抱怨,我觉得也是不可否认的)。每个初涉泛型的人可能都会经历这样的阶段,什么是泛型,为什么会有泛型,怎么样使用泛型,它能给我们带来什么?等等
其实早在JDK1.5之前,java还不存在泛型,但java语言是强类型,编译型语言,而且我们强调要尽早的(编译期间)发现异常解决异常,然而没有泛型我们可能会遇到这样的问题:
List fruits = new ArrayList();
fruits.add(new Apple());
fruits.add(new Egg());
fruits.add(new People());
((Fruit) fruits.get(2)).juice();//it's horrible,and exception will be checked delaying runtime
以上的代码告诉我们,在没有泛型时我们可以将任何的类型添加到一个水果篮中,但我们希望的只是水果,可此时编译器无法发现问题的所在,因为编译器对此毫无所知,你必须到运行时才能发现问题的所在,这可不是我们想要的结果。
为了避免这样的不爽,SUN 的工程师们从JDK1.5开始引进了泛型来解决一系列无法在编译器发现的类型问题,但将上面的代码通过泛型进行优化后我们会发现:
List<Fruit> fruits = new ArrayList <Fruit>();
fruits.add( new Apple());
fruits. add(new Egg());//bad fruit
fruits. add(new People());//bad fruit
fruits.get(0).juice();//it's fruit ,you can get juice
此时你只能将水果放入你的水果篮,因为这个水果篮是编译器强制要求的,我们通过
可能你会好奇JAVA 是如何通过泛型来进行编译器的安全监察的,其实这只是编译器的一个小手段,它会透明的为我们进行类型的强制转换,而强制转换的依据便是你声明在尖括号中的类型。看一下编译后再反编译的的代码你就会明白了:
List fruits = new ArrayList();
fruits.add(new Apple());
Fruit fruit = (Fruit)fruits.get(0);
你可能会有点失望,为什么还是类型装换,这跟我们自己做的有什么区别的,相信很多人都会抱怨,尤其是熟悉C++模板的coder。但我们也不能过于责备SUN的工程师们,因为这是为了兼容以前的JDK版本,为了让低版本中编写的代码依旧可以运行在支持泛型的JVM上,这是一个对用户负责的考虑。让我们来看一下编译器是怎么处理我们的泛型代码的:
当我们写好了一个泛型代码时,尖括号中的类型总是会通知编译器,我们需要的类型,编译器便会在需要类型安全的地方进行类型转化,而当他发现你使用的不是匹配类型时便会抛出一个编译时异常,而不需要等到运行时。
当然泛型并不只是在容器中使用(虽然这是应用最多的地方,尤其是对应用开发人员)但我们还有泛型类,泛型方法,泛型数组需要理解,不过中心思想还是上面讲到的这些,都是编译器把戏,我们需要了解的是为了达到一个目的,编译器做了什么,为什么这么做就足够了。