泛型引进的主要目的是:用来指定容器要持有什么样类型的对象,而且由编译器保证类型的正确性
(编译阶段就能检查出错误)。
泛型在
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){
(Apple)list.get(1);
}
else
if
(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>
是装苹果的容器,
java为了保证类型安全
,不允许装水果的容器指向装苹果的容器
。
还有一个问题是
我一会要去买一种特定的水果,具体是哪一种,只有到买的时候才能知道,现在要准备一个放该特定水果的容器,该怎么办?
换句话说是,我们大部分的时候需要的灵活性 比不可变类型要高。
类似于
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
忘了泛型的存在吧,表面上的事都是骗人的,哥不干了。。。。