java泛型 null_java泛型详解

1. extends与super

extends关键字限制了泛型类可以使用的泛型参数类型的上限(Upper Bound)

super关键字限制了泛型类可以使用的泛型参数类型的下限(Lower Bound)

先定义几个简单的Class

classFood {

}class Fruit extendsFood {

}class Meat extendsFood {

}class Apple extendsFruit {

}class Orange extendsFruit {

}class Pork extendsMeat {

}class Beef extendsMeat {

}

extends解析

直接赋值的限制

以泛型类List为例,如果我们声明下面这样的一个List

List extends Fruit> fruitList;

那么后续给这个fruitList赋值时,这个值允许使用的泛型参数的上限就是Fruit,也就是Fruit或者Fruit的子类。所以下面的代码是可以编译通过的

fruitList = new ArrayList();

fruitList= new ArrayList();

下面的代码则会引发编译失败

fruitList = new ArrayList();//Food不是Fruit的子类,编译失败

泛型方法参数的限制

在上面的例子中,fruitList的含有泛型参数的方法,例如add,是受到极大的限制的:

fruitList.add(new Object());//编译错误

fruitList.add(new Food());//编译错误

fruitList.add(new Fruit());//编译错误

fruitList.add(new Apple());//编译错误

fruitList.add(null);//编译通过

可以发现,除了不带有Class类型信息的null,任何Class(甚至包括Object)的实例,都不能作为add方法的泛型参数,这是为什么呢?

因为extends关键字只是限制了泛型类可以使用的泛型参数的上限,编译器并不知道fruitList实际指向的实例使用的泛型参数具体是什么,可能是Fruit,也可能是Apple或者Orange。

这句话不太好理解,但是如果把fruitList直接替换成已经正确设置了泛型参数的泛型类的实例,然后调用泛型方法,可能会比较好懂。

例如在泛型参数为Orange时,上面的代码可以转换为下面的形式:

new ArrayList().add(new Object());//编译错误

new ArrayList().add(new Food());//编译错误

new ArrayList().add(new Fruit());//编译错误

new ArrayList().add(new Apple());//编译错误

new ArrayList().add(null);//编译通过

也就是说,编译器采用了非常保守的策略来确保类型安全(只允许发生隐式的从子类向父类的类型转换)。

泛型方法返回值的限制

在上面的例子中,由于可以确定fruitList对应的实例使用的泛型参数必然是Fruit或者Fruit的子类,所以如果有方法的返回值是泛型参数,那么可以确定这个返回值必然也是Fruit或者Fruit的子类了

Fruit fruit = fruitList.get(0);

上面这行代码是IDEA的自动补全返回值类型的效果。

super解析

直接赋值的限制

以泛型类List为例,如果我们声明下面这样的一个List

List super Fruit> fruitList;

那么后续给这个fruitList赋值时,这个值允许使用的泛型参数的下限就是Fruit,也就是Fruit或者Fruit的父类。所以下面的代码是可以编译通过的

fruitList = new ArrayList();//编译通过

fruitList = new ArrayList();//编译通过

而下面的代码则会引发编译失败

fruitList = new ArrayList();//编译错误

泛型方法参数的限制

在上面的例子中,fruitList的含有泛型参数的方法,例如add,也受到了一定的限制:

fruitList.add(new Object());//编译错误

fruitList.add(new Food());//编译错误

fruitList.add(new Fruit());//编译通过

fruitList.add(new Apple());//编译通过

fruitList.add(null);//编译通过

可以发现,只允许使用Fruit或者Fruit的子类的实例作为参数,这又是为啥呢?

因为super关键字是限制了泛型类可以使用的泛型参数的下限,泛型参数无论是什么,都必然是Fruit或者Fruit的父类,这必然也是Fruit的子类的父类了。

所以只要给泛型方法传入Fruit或者Fruit子类的实例作为参数,最多只会引发隐式类型转换,不会导致异常。

以泛型参数为Food为例,把上面的代码转换一下:

new ArrayList().add(new Object());//编译错误

new ArrayList().add(new Food());//编译通过,但是如果换另外一种父类就出错了

new ArrayList().add(new Fruit());//编译通过

new ArrayList().add(new Apple());//编译通过

new ArrayList().add(null);//编译通过

这也是编译器为了确保类型安全作出的努力

泛型方法返回值的限制

在上面的例子中,由于无法确定fruitList对应的实例使用的泛型参数到底是什么玩意(可能是Fruit,也可能是Food,甚至可能是Object),所以只能用万能的Object作为返回值类型了:

Object object = fruitList.get(0);

上面这行代码是IDEA的自动补全返回值类型的效果。

PECS法则

Producer->extends    Consumer->super

如果你希望某个泛型类只能生产(方法有泛型返回值)受限定的元素(Producer),那么就该用extends关键字来做限定

如果你希望某个泛型类只能消费(方法有泛型参数)受限定的元素(Consumer),那么就该用super关键字来做限定

小结

extends和super的限定初看非常难懂,但是如果把泛型类的泛型参数试着实例化并且展开一下,就好理解多了。

这些奇怪的限制,主要是为了在任何情况下都能保证类型安全(只发生隐式的子类向父类的转换)

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值