4. System.out.println("Sting");
5. }
6. publicvoidtest(List<Integer>li){
7. System.out.println("Integer");
8. }
9. }
那么这就有个问题了,既然在编译的时候会在方法和类中擦除实际类型的
信息,那么在返回对象时又是如何知道其具体类型的呢?如 List<String>编译
后会擦除掉 String 信息,那么在运行时通过迭代器返回 List 中的对象时,又是
如何知道 List 中存储的是 String 类型对象呢?
擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象
进入和离开方法的地点,这正是编译器在编译期执行类型检查并插入转型代码
的地点。泛型中的所有动作都发生在边界处:对传递进来的值进行额外的编译
期检查,并插入对传递出去的值的转型。
3.泛型和子类型
为了彻底理解泛型,这里看个例子:(Apple 为 Fruit 的子类)
Java 代码
1. List<Apple>apples=newArrayList<Apple>();//1
2. List<Fruit>fruits=apples;//2
第 1 行代码显然是对的,但是第 2 行是否对呢?我们知道 Fruit fruit =
new Apple(),这样肯定是对的,即苹果肯定是水果,但是第 2 行在编译的时
候会出错。这会让人比较纳闷的是一个苹果是水果,为什么一箱苹果就不是一
箱水果了呢?可以这样考虑,我们假定第 2 行代码没有问题,那么我们可以使
用语句 fruits.add(new Strawberry())(Strawberry 为 Fruit 的子类)在
fruits 中加入草莓了,但是这样的话,一个 List 中装入了各种不同类型的子类
水果,这显然是不可以的,因为我们在取出 List 中的水果对象时,就分不清楚
到底该转型为苹果还是草莓了。
通常来说,如果 Foo 是 Bar 的子类型,G 是一种带泛型的类型,则
G<Foo>不是 G<Bar>的子类型。这也许是泛型学习里面最让人容易混淆的
一点。
4.通配符
4.1 通配符?
先看一个打印集合中所有元素的代码。