1700447337
1700447338
public static void main(String[]args){
1700447339
1700447340
Base base=new Sub();
1700447341
1700447342
}
1700447343
1700447344
base变量是否发生了协变?是的,发生了协变,base变量是Base类型,它是父类,而其赋值却是子类实例,也就是用窄类型覆盖了宽类型。这也叫多态(Polymorphism),两者同含义,在Java世界里“重复发明”轮子的事情多了去了。
1700447345
1700447346
说了这么多,下面再来想想泛型是否也支持协变和逆变,答案是:泛型即不支持协变,也不支持逆变。很受伤是吧?为什么会不支持呢?
1700447347
1700447348
(1)泛型不支持协变
1700447349
1700447350
数组和泛型很相似,一个是中括号,一个是尖括号,那我们就以数组为参照对象,看如下代码:
1700447351
1700447352
public static void main(String[]args){
1700447353
1700447354
//数组支持协变
1700447355
1700447356
Number[]n=new Integer[10];
1700447357
1700447358
//编译不通过,泛型不支持协变
1700447359
1700447360
List<Number>ln=new ArrayList<Integer>();
1700447361
1700447362
}
1700447363
1700447364
ArrayList是List的子类型,Integer是Number的子类型,里氏替换原则(Liskov Substitution Principle)在此处行不通了,原因就是Java为了保证运行期的安全性,必须保证泛型参数类型是固定的,所以它不允许一个泛型参数可以同时包含两种类型,即使是父子类关系也不行。
1700447365
1700447366
泛型不支持协变,但可以使用通配符(Wildcard)模拟协变,代码如下所示:
1700447367
1700447368
//Number的子类型(包括Number类型)都可以是泛型参数类型
1700447369
1700447370
List<?extends Number>ln=new ArrayList<Integer>();
1700447371
1700447372
“?extends Number”表示的意思是,允许Number所有的子类(包括自身)作为泛型参数类型,但在运行期只能是一个具体类型,或者是Integer类型,或者是Double类型,或者是Number类型,也就是说通配符只是在编码期有效,运行期则必须是一个确定类型。
1700447373
1700447374
(2)泛型不支持逆变
1700447375
1700447376
Java虽然可以允许逆变存在,但在对类型赋值上是不允许逆变的,你不能把一个父类实例对象赋值给一个子类类型变量,泛型自然也不允许此种情况发生了,但是它可以使用super关键字来模拟实现,代码如下。
1700447377
1700447378
//Integer的父类型(包括Integer)都可以是泛型参数类型
1700447379
1700447380
List<?super Integer>li=new ArrayList<Number>();
1700447381
1700447382
“?super Integer”的意思是可以把所有Integer父类型(自身、父类或接口)作为泛型参数,这里看着就像是把一个Number类型的ArrayList赋值给了Integer类型的List,其外观类似于使用一个宽类型覆盖一个窄类型,它模拟了逆变的实现。
1700447383
1700447384
泛型既不支持协变也不支持逆变,带有泛型参数的子类型定义与我们经常使用的类类型也不相同,其基本的类型关系如表7-1所示。
1700447385
1700447386
[
上一页 ]
[ :1.700447337e+09 ]
[
下一页 ]