先提问:
数组存储与集合存储在编译期的区别
集合中对于泛型的使用规则
<? extends T>和<T>的区别与二者何时被擦除
正文:
先给出3个实验类
class Fruit{ }
class Orange extends Fruit{ }
class Apple extends Fruit{ }
数组:
编译器对代码信息了解的足够多,一些地方允许编译通过
以下4行编译均通过:
Fruit[] fruit = new Apple[10];
fruit[0] = new Apple();
fruit[0] = new Orange();
fruit[0] = new Fruit();
但是很显然,运行时3,4行的运行是不通过的,你无法将一个Orange放到一个Apple数组中,你也无法讲一个父类对象放到一个子类对象数组中
集合:
编译器对代码了解的不够多,相对于数组来说有些地方不允许编译通过
下面这行编译不通过
List<Fruit> flist = new ArrayList<Apple>
编程思想上的解释是:大家第一反应是“不能讲一个Apple容器赋值给一个Fruit容器”,但正确说法是“不能把一个涉及Apple的泛型赋给一个涉及Fruit的泛型”,就像在数组中的情况一样,编译期对代码的了解足够多,可以确定所涉及到的容器,那么他可能会留下一些雨滴,但是它不知道任何有关这方面的信息,因此他拒绝向上转型。然而实际上这根本不是向上转型——Apple的List不是Fruit的List。Apple的List将持有Apple和Apple的子类型,而Fruit的List将持有任何类型的Fruit,诚然其中包含Apple,但他不是一个Apple的List,他仍然是Fruit的List,Apple的List在类型上不等价与Fruit的List,即使Apple是一种Fruit类型
换句话说,在泛型确定之后,不同的泛型的集合之间是不能转换的,即使一个泛型能上转型成另一个泛型
下面讨论<? extends Fruit>
<? extends T>的作用是为了让泛型在符合继承的条件下正确准确的擦除
List<Fruit1> flist = new ArrayList<Apple1>();
List<? extends Fruit1> flist1 = new ArrayList<Apple1> ();
(1)第一句是不允许的,已经说了,容器级别不能直接由编译期识别向上转型
(2)但是第二句是允许的,他在擦除时将泛型确定为了Apple1,编译通过也是因为Apple1符合 extends Fruit1的规则
(3)可是,你无法向里面add对象,编译期无法在你加东西地时候确定你加的到底是什么类型,他无法准确擦除泛型,所以编译失败
但是有一点,你要从里面拿东西,那是可以的,因为无论如何,它至少能保证拿出来的是Fruit类型
public
class
GenericReading {
//方法为泛型
static
<T> T readExact(List<T>
list
){
return
list
.get(0);
}
static
List<Apple>
apples
= Arrays.asList(
new
Apple());
static
List<Fruit>
fruit
= Arrays.asList(
new
Fruit());
static
void
f1(){
Apple1
a
= readExact(
apples
);
Fruit1
f
= readExact(
fruit
);
//方法级别上的泛型,接收时确定泛型为Apple1,返回时也返回Apple1,只是f作为Fruit1类型
//接受,所以是成立的
f
= readExact(
apples
);
}
//----------------------------------------------------------------------------------------
//类中带泛型
static
class
Reader<T>{
T readExact(List<T>
list
){
return
list
.get(0);
}
}
static
void
f2(){
Reader<Fruit>
reader
=
new
Reader<Fruit>();
Fruit
f
=
reader
.readExact(
fruit
);
//Fruit1 f1 = reader.readExact(apples);
//这句不通过,因为在定义时便已经确定泛型T为Fruit1,这里使用的时候传入的泛型T又为Apple1
//二者不一致,编译不通过
}
//----------------------------------------------------------------------------------------
static
class
Reader2<T>{
T ReaderExact(List<?
extends
T>
list
){
return
list
.get(0);
}
}
static
void
f3(){
Reader2<Fruit>
reader2
=
new
Reader2<Fruit>();
Fruit1
f
=
reader2
.ReaderExact(
fruit
);
Fruit1
f2
=
reader2
.ReaderExact(
apples
);
//这句编译通过是因为虽然在创建对象时确定了泛型T为Fruit,但是在方法中参数list使用的泛型
//并不是T,而是<? extends T>,又因为传入的是apples,对应的泛型为Apple,且<Apple extends Fruit>
//符合规范,最后返回值自动向上转型成T,也就是Fruit输出出去
}
}
类中<? extends T>的参数不会因为类中的泛型T被确定而确定,只有使用的时候才会确定,在执行完最后
该方法最后一句时,Reader2类被擦除泛型成
static class Reader2{
Fruit ReaderExact(List <Apple> list){
return list.get(0);
}
}