java泛型的一些总结
泛型引进的主要目的是:用来指定容器要持有什么样类型的对象,而且由编译器保证类型的正确性(编译阶段就能检查出错误)。
泛型在java1.5之前并不存在,是在1.5的时候引入的,这说明之前的容器肯定有明显的不足之处。
原生态类型容器的不足:
List list = new ArrayList();
list.add(new Apple());
list.add(new Dog());
list.add(new Person());
//从list中取出元素的时候,只能自己手工判断了
if(list.get(1)instanceof Apple){
}elseif (list.get(1)instanceof Dog){
}else{
}
实际我们应用容器持有对象时,其实应该像使用数组一样,可以指定所持有对象的数据类型。并且一个特定数组一般只持有一种数据类型的数据(包括该数据类型的子类)。
如
Fruit [] fruit = new Fruit[10];
fruit[0] = new Fruit();
fruit[1] = new Apple();
泛型因此被引入,以后可以用以下语句:
List<Fruit> fruitList = new ArrayList<Fruit>();
fuitList.add(new Fruit());
fuitList.add(new Apple());
按说此问题就应该都解决了,可是因为多态的存在,使得泛型产生了一系列问题。看下面的语句:
Fruit [] fruit = new Apple[10];
fruit[0] = new Apple();
fruit[1] = new Banana();
编译时,类型检查并没有问题,但运行时此处的实际的fruit[]是Apple[],只能装苹果,现在让他装Banana,肯定不行。
还记得泛型的主要目的不?
重复一遍
用来指定容器要持有什么样类型的对象,而且由编译器保证类型的正确性
即泛型要在编译阶段解决这个不安全问题。
Java中的做法是让:
List<Fruit> fruitList = new ArrayList<Apple>();在编译阶段报错,这样就成功解决了上述问题。但又产生了其他问题。
因为List<Fruit> fruitList = new ArrayList<Apple>();的直观理解就是,你有一个fruit的容器,但是该容器不能放苹果。你可以推断该容器也不能放香蕉,橘子,葡萄,等等任何一种具体的水果。这显然与常识不符。
但是你错了,因为上述理解用的语句应该是:
List<Fruit> fruitList = new ArrayList<Fruit>();
而List<Fruit> fruitList = new ArrayList<Apple>();的正确理解是,fruitList是一个装水果的容器,并不是装水果容器(ArrayList<Apple>是装苹果的容器)的容器。
还有一个问题是我一会要去买一种特定的水果,具体是哪一种,只有到买的时候才能知道,现在要准备一个放该特定水果的容器,该怎么办?
类似于List<Fruit> fruitList = new ArrayList<Fruit>();的语句不安全,存在最开始分析的手工判断的缺点,
类似于List<Fruit> fruitList = new ArrayList<Apple>();在java中又不合法。
于是大牛们创造了:
List<? Extends Fruit> fruitList = new ArrayList<Apple>();
他们规定<? Extends Fruit>表示不是所有Fruit子类的集合,而是Fruit子类的任意的特定的一种。
体会任意的特定的一种的含义。
因为fruitList是任意的特定的一种水果,现在还不知道该种水果到底是哪一种,所以它不能add进去任何东西。
该类型的主要作用是给 fruitList赋值,然后从中取值。
接下来最让人头疼的问题来了:
泛型是1.5以后引入,在此之前的代码应该在泛型引入以后还能用,所以jvm不能改,泛型只能是骗骗编码的程序员,即泛型只是在编译阶段编译器把一些强制类型转换自动插入到你的代码中,到运行阶段时根本不存在任何关于泛型的痕迹。
泛型要是跟反射一起等运行时确定类型的一起方法一起使用时,我只想说 MLGB忘了泛型的存在吧,表面上的事都是骗人的,哥不干了。。。。