extends关键字限制了泛型类可使用的泛型参数类型的上限(Upper Bound)安全
super关键字限制了泛型类可使用的泛型参数类型的下限(Lower Bound)ui
先定义几个简单的Classspa
class Food { } class Fruit extends Food { } class Meat extends Food { } class Apple extends Fruit { } class Orange extends Fruit { } class Pork extends Meat { } class Beef extends Meat { }
直接赋值的限制.net
以泛型类List为例,若是咱们声明下面这样的一个Listcode
List<? extends Fruit> fruitList;
那么后续给这个fruitList赋值时,这个值容许使用的泛型参数的上限就是Fruit,也就是Fruit或者Fruit的子类。因此下面的代码是能够编译经过的blog
fruitList = new ArrayList<Apple>(); fruitList = new ArrayList<Orange>();
下面的代码则会引起编译失败get
fruitList = new ArrayList<Food>();//Food不是Fruit的子类,编译失败
泛型方法参数的限制编译器
在上面的例子中,fruitList的含有泛型参数的方法,例如add,是受到极大的限制的:it
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<Orange>().add(new Object());//编译错误 new ArrayList<Orange>().add(new Food());//编译错误 new ArrayList<Orange>().add(new Fruit());//编译错误 new ArrayList<Orange>().add(new Apple());//编译错误 new ArrayList<Orange>().add(null);//编译经过
也就是说,编译器采用了很是保守的策略来确保类型安全(只容许发生隐式的从子类向父类的类型转换)。
泛型方法返回值的限制
在上面的例子中,因为能够肯定fruitList对应的实例使用的泛型参数必然是Fruit或者Fruit的子类,因此若是有方法的返回值是泛型参数,那么能够肯定这个返回值必然也是Fruit或者Fruit的子类了
Fruit fruit = fruitList.get(0);
上面这行代码是IDEA的自动补全返回值类型的效果。
直接赋值的限制
以泛型类List为例,若是咱们声明下面这样的一个List
List<? super Fruit> fruitList;
那么后续给这个fruitList赋值时,这个值容许使用的泛型参数的下限就是Fruit,也就是Fruit或者Fruit的父类。因此下面的代码是能够编译经过的
fruitList = new ArrayList<Fruit>();//编译经过 fruitList = new ArrayList<Food>();//编译经过
而下面的代码则会引起编译失败
fruitList = new ArrayList<Apple>();//编译错误
泛型方法参数的限制
在上面的例子中,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<Food>().add(new Object());//编译错误 new ArrayList<Food>().add(new Food());//编译经过,可是若是换另一种父类就出错了 new ArrayList<Food>().add(new Fruit());//编译经过 new ArrayList<Food>().add(new Apple());//编译经过 new ArrayList<Food>().add(null);//编译经过
这也是编译器为了确保类型安全做出的努力
泛型方法返回值的限制
在上面的例子中,因为没法肯定fruitList对应的实例使用的泛型参数究竟是什么玩意(多是Fruit,也多是Food,甚至多是Object),因此只能用万能的Object做为返回值类型了:
Object object = fruitList.get(0);
上面这行代码是IDEA的自动补全返回值类型的效果。
Producer->extends Consumer->super
若是你但愿某个泛型类只能生产(方法有泛型返回值)受限定的元素(Producer),那么就该用extends关键字来作限定
若是你但愿某个泛型类只能消费(方法有泛型参数)受限定的元素(Consumer),那么就该用super关键字来作限定
extends和super的限定初看很是难懂,可是若是把泛型类的泛型参数试着实例化而且展开一下,就好理解多了。
这些奇怪的限制,主要是为了在任何状况下都能保证类型安全(只发生隐式的子类向父类的转换)