泛型 转型的可行性——数组的转型
声明了几个跟水果相关的类
可以把派生类数组赋值给基类数组——记住这不是向上转型,虽然Java编译期允许,但是确实不是向上转型,如果不相信,我们可以对比泛型集合的向上转型。
泛型转型的可行性——泛型的转型
我们不能把跟Apple相关的泛型转型为跟Fruit相关的泛型。这跟数组的向上转型类似,但是由于集合不是Java的内置类型,并且泛型是通过擦除实现的,编译器无法知道集合所持有的对象的类型,所以不允许这种转型。
从另一种意义来说,Apple的List不是Fruit的List,Apple的数组也不是Fruit的数组。我们现在是在讨论集合或数组的类型,而不是集合或数组持有对象的类型。如果数组不是内置类型,如果数组不知道自己持有对象的类型,那么Apple的数组也是无法向上转型为Fruit的数组。
泛型转型的可行性——数组和泛型转型的总结
千万不要认为派生类数组转型为基类数组是向上转型,是的,大家都这么称呼,但是只要跟泛型的转型进行对比,就会明白,这是向上转型的滥用。
泛型的转型——extends通配符——语法
List<? extends Fruit> flist = new ArrayList<Apple>();
通配符是不能用于声明泛型类和泛型方法的,只能用于声明对象。
泛型的转型——extends通配符——含义
flist的类型是List<? extends Fruit>,表示Fruit子类的list,千万不要认为这个List可以持有任何类型的Fruit。通配符是指向某个确定的Fruit子类。List<? extends Fruit>表示某个Fruit子类的List,具体是哪个子类,没有明确指明。
本来泛型的通配符语法就是为了向上转型而使用的,List<? extends Fruit>表示某个特定的Fruit子类的List,虽然由于转型之后我们不清楚到底是哪个Fruit的子类。
来来,跟数组的向上转型对比一下:
fruit允许插入各种Fruit子类的对象,但是在泛型通配符中,Java不允许插入任何对象,因为泛型的一个目的就是要把运行时检查转移到编译期检查,既然不清楚是哪个Fruit子类,我们怎么能够插入任何Fruit子类的对象呢?
泛型的转型——extends通配符——诡异的地方
泛型向上转型之后什么对象都不能加入List了,但是get得到的对象一定是Fruit类型。
泛型的转型——extends通配符——诡异的地方的解决方案
当你在创建自己的泛型类的时候,记得要处理好通配符的情况:
看清楚,List中的contains和indexOf的参数是Object,如果是参数类型的话,这两个方法就没法调用了。
泛型的转型——super通配符
List<? super Apple> apples
? super Apple表示Apple的任意父类。
泛型的转型——super通配符和extends通配符的比较
List<? super Apple> apples是持有Apple任意父类类型对象的List;
List<? extends Apple> apples是持有Apple任意子类类型对象的List;