在学习java泛型时 ,看到了泛型的一个例子List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
fruits.add(new Strawberry());其中,Fruit是父类、Apple和Strawberry是子类,add操作执行错误,给出的解释是“这个? extends T 通配符告诉编译器我们在处理一个类型T的子类型,但我们不知道这个子类型究竟是什么。因为没法确定,为了保证类型安全,我们就不允许往里面加入任何这种类型的数据。”我不明白编译器怎么会不知道这个子类型是什么呢?如果不知道的话,语句List<? extends Fruit> fruits = apples;为什么不报错呢?
List<? extends Fruit> fruits = apples;
fruits.add(new Strawberry());其中,Fruit是父类、Apple和Strawberry是子类,add操作执行错误,给出的解释是“这个? extends T 通配符告诉编译器我们在处理一个类型T的子类型,但我们不知道这个子类型究竟是什么。因为没法确定,为了保证类型安全,我们就不允许往里面加入任何这种类型的数据。”我不明白编译器怎么会不知道这个子类型是什么呢?如果不知道的话,语句List<? extends Fruit> fruits = apples;为什么不报错呢?
而当是成员方法时,他保持的是运行时类型
List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples; //这个fruits = apples保持的是编译时类型时类型 ,把List<Apple>赋值给 List<? extends Fruit>当然没问题
fruits.add(new Strawberry()); //这个fruits.add()保持的是运行时类型,fruits运行时他的类型是List<Apple>当然运行出错了,但是在编译时就错误了,更别提运行时了,编译时编译器不会把List的泛型去掉,他会跟着List<? extends Fruit>去判断,所以你编译都过不了,当编译成class文件后他就会自动把泛型去掉,任何同类型的集合的字节码是一个,你可以通过反射给他注入你行要得值
1、首先List<Apple> 是 List<? extends Fruit> 的子类型,一个父类型的变量fruits 当然能指向一个子类型的变量引用apples;2、“这个? extends T 通配符告诉编译器我们在处理一个类型T的子类型,但我们不知道这个子类型究竟是什么。”
这是正确的,有上面第一点可以知道List<Strawberry>也是List<? extends Fruit>的子类型,你也说了,泛型能保证类型安全,而fruits变量的静态类型是List,有个? extends Fruit的类型参数,编译器能确定的就是这个变量的类型是List,其能存储的对象类型是Fruit的某种字类型或Fruit;所以这里fruits.add(new Strawberry());是不能操作的!
如果你不写的话,它默认是 List<? entends Object>
List<? extends Fruit> fruits = apples;
这两句确定了fruits这个list此时只能存储Apple了,不可以再存储其他子类了
List<? extends Fruit> fruits = apples;只表示fruits这个变量可以引用apples;
也即:
List<Strawberry> strawberrys = new ArrayList<Strawberry>();
List<? extends Fruit> fruits = strawberrys;
也是可以的;其实编译器根据 ? extends Fruit 只知道:list中可以是Fruit或它的任何某种子类型对象,但具体是什么类型是不确定的,所以你add(new Fruit()); add(new Strawberry()); add(new Apple()); 编译器都是会报错的;如果难理解,反过来理解:
比如fruits.add(new Strawberry()),这里是add一个Strawberry类型对象,而fruits要求add的对象类型必须是Fruit或它的任何某种子类型对象,这个某种类型能用Strawberry或Fruit或Apple来表示吗?
我认为这里的fruits表示的一个存储fruit子类的集合,而apples里面存贮的就是fruit的子类apple,所以没有错误
List<? extends Fruit>
大于
List<Apple> apples
大于
new ArrayList<Apple>方向是祖先类=>子孙类但是在涉及容器是否可添加元素判断时,因编译时容器元素类型只是根据? extends Fruit来确定,因此会报不确定子类型,比如即使改成这样也会编译报错:
List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
fruits.add(new Apple()); //compile error这个10楼说过了但我们知道实际上这个List要装的就是Apple,但在编译时就不知道。
List<? extends Fruit>super thanList<Apple>super thanArrayList<Apple>
List<Apple> apples = new ArrayList<Apple>();
// 将apples 的引用给 fruits 没问题 , 因为apples的定义为List<Apple> , 而Apple是Fruit的子类
List<? extends Fruit> fruits = apples;
//经过上一条代码,fruits 就等于 fruits = new ArrayList<Apple>(); 只能往fruits 里面放Apple//对象了
//但是这里放Strawberry对象,所以就报错了。
fruits.add(new Strawberry());
?表示类型未知,为防止运行时类型转换出错,有限制的通配符列表不接受除了null之外的任何其他值