泛型在jdk1.5出来的,以为自己很
理解它了,至少看看代码,写写基本的没有问题。
由于是个新事物,对于我这种从jdk1.1过来的人来说还是一个思维上的跳跃。最近写了一些代码,好好整理了一下。
为什么会出现泛型
我喜欢事事问为什么,要不然就觉得心里不踏实。
我已经不止一次的提到过,任何的
新技术的出现都是伴随着解决一个问题来的。
那泛型解决了什么?
我们记得在以前的
编码过程中,exception中无非就那几种是比较常见和头疼的。第一大非NullPointerException莫属了,我就不多说了,接着就是ClassCastException,因为我们的代码中往往为了所谓的
扩展性(可能是过度设计产生的),用了很多Object类作为参数输入,没有泛型之前,我们的
处理方法是到处用强制转换,如果保险点就加很多的instanceof.所以这个Class类型匹配要多烦有多烦。
好了,泛型来了,它的最大特点就是要规范你的编码风格,不允许你乱定义你的类型,用错了就编译不过,不用就给你一个警告,哈哈。
总之,泛型的作用就是使代码得到的信号类型一致,从而使行为一致。
怎么要使用泛型
泛型分为两步:申明 + 使用
申明:两个个地方可以申明,类定义中申明,方法定义申明。(有人提出三个地方,
接口定义,但是我想接口也是类嘛)
为什么就这两个地方?有什么好处?
如下类定义中申明了一个T类型,这样就可以在这个类中的任何地方用这个T作为类型,就像一辆车,框架有了,但是什么材料做的不知道,可以是铁的,也可以是铝的,所以我们就用”材料T“
来代替材料类型。
public class Car<T extends Metal>{}
那怎么使用这个材料T泛型呢?如果我们new Car的时候不指定材料T的具体,是无法造出汽车的,巧妇难为无米之炊啊。所以我们使用泛型的时候就需要具体化它。
public class Fe extends Metal{}
public class Al extends Metal{}
Car<Fe> car1=new Car<Fe>();
Car<Al> car2=new Car<Al>();
这样车车就造出来了,很酷吧?
但是上述有个不爽的地方就是,每次造车的时候必须告诉它是什么材质的,但是有时候我们根本不关心材质的情况下,就显得多余了。比如我要知道每一种车的颜色而不是材质。如果我们按照上述的类申明的泛型,那么可以在class定义后面加个颜色S的泛型(支持逗号),但是如果下次不要颜色,要尺寸,我是不是还要加个尺寸泛型M?
是不是很多啊,所以我想除了一些基本的类型外,其他局部的特性就不用放在类申明里面了,可以放在方法申明里面,怎么做呢?请看
public class Car{
public <S extends Color> S getColor(){
}
public <S extends Color> void setColor(S s){
}
}
其中的S是专属于方法的,这个方法还可以是static的。
那怎么使用呢?很简单,如下:
public class Red extends Color{}
Car car3=new Car();
car3.setColor(new Red());
核心思想
说了这么多,我想泛型的核心思想就是规范类和方法的行为。只要两个类或者方法有相同的逻辑,哪怕是一点点相像的地方,我们都可以用一个泛型定义来申明它,这样就使得代码十分简洁安全。
在这里我要提一下复杂关系情况下的泛型。因为任何技术总是原理简单,到复杂环境下就丧失理智,摸不着头脑。
举个
例子:父母--子女--性别
父母有子女,子女有性别
如果不用泛型,我们会分别对男孩,女孩分别写逻辑和类。
比如:
public static class Gender{}
public static class Male extends Gender{}
public static class Female extends Gender{}
public static class Child{
public Gender gender;
}
public static class Boy extends Child{}
public static class Girl extends Child{}
public static class Father{
private Child child;
public Boy getBoy(){
return (Boy) child;
}
public Girl getGirl(){
return (Girl) child;
}
public void setBoy(Boy boy){
this.child=boy;
}
public void setGirl(Girl girl){
this.child=girl;
}
}
是不是觉得两个get/set方法结构一样,但是非要实现,而且方法名字必须不一样,因为类
重载不能以返回值来区分。
如果用泛型,就简单清晰了。
首先申明:
public interface Child<G> {}
指定child,但是性别不知道,所以用了“性别G”来代替
接着
public class Father<C extends Child<?>> {
private List<C> children = new ArrayList<C>();
public void addChild(C c) {
this.children.add(c);
}
public List<C> getChildren() {
return this.children;
}
}
指定了father,但是不知道子女是男孩还是女孩,所以用了"孩子C"来代替,并且extends Child,意思是至少我知道是个孩子,孩子里面还有个?号,意思是我知道是个孩子,但是我不知道孩子是男是女。
好了,申明好了,接着我们就开始用了。
Father<Boy> father1=new Father<Boy>();
Father<Girl> father2=new Father<Girl>();
father1.addChild(new Boy());
father2.addChild(new Girl());
是不是很简洁?
这就是面向对象语言的魅力。