Java中泛型extends与super的区别
从泛型说起
1.Java从1.5开始引入了泛型,泛型-也就是参数化类型。在Java容器类中大量的使用了泛型这种思想。泛型的利用好处很多:
减少了代码中大量的强制性类型转换,可读性更高
通用性更强,
类型更安全,提高性能等
2.需要明确的是Java的泛型是伪泛型,泛型只在编译时期有效,在编译器正确的检验出泛型的结果后随即泛型的信息就会被擦除。并且泛型的信息不会进入到运行时阶段。看一个例子:
(福利推荐:阿里云、腾讯云、华为云服务器最新限时优惠活动,云服务器1核2G仅88元/年、2核4G仅698元/3年,点击这里立即抢购>>>)
public static void main(String[] args) { List<Integer> integers = new ArrayList<>(); integers.add(1); List<String> strings = new ArrayList<>(); strings.add("1"); System.out.println(integers.getClass().equals(strings.getClass())); }
3.泛型信息被擦除成原始的List,在实际开发中,泛型擦除会带来一些问题,既然运用了无界泛型,并且由于泛型擦除的存在,那么只能调用Object的自身方法。这个时候我们就需要给泛型指定边界,将泛型限制在一定的范围。
上界通配符<? extends T>
1.上界,顾名思义,最高只能到T,也即类型只能是T或者是T的子类。以水果这个常见的类型举例,水果,热带水果,苹果,橘子,香蕉等。可见水果作为整个超类依次往下。
public class Fruit {} public class TropicalFruit extends Fruit {/** 热带水果 */} public class Apple extends TropicalFruit {} public class RedApple extends Apple {} public class Orange extends TropicalFruit {} public class Banana extends TropicalFruit {}
2.拿集合List举例:
public static void main(String[] args) { List<? extends TropicalFruit> appleList = new ArrayList<>(); //报错,提示无法添加 appleList.add(new Apple()); appleList.add(null); }
3.之前提到<? extends T>上界通配符规定的是最高到T类型,而我们这里List<? extends TropicalFruit>为什么不能够添加Apple呢?null是可以的,因为null表示任何类型。这里定义了一个装热带水果(TropicalFruit)的集合,按理说苹果(Apple)是可以放进去的,这是对于概念混淆可能会犯这样的错误。其实这里很好理解,首先编译器其实不聪明,List<? extends TropicalFruit>编译器理解的是,只要是热带水果本身或者其子类都是可以存放的,那么问题来了,假如是一个List,此时我想放香蕉那显然是不行的。类型转化错误,但是编译器知道的是都是热带水果TropicalFruit,有可能是装苹果的袋子,也有可能是装香蕉的袋子,具体是不知道的,那么添加操作显然是不安全的。索性编译时期直接报错。
下界通配符<? super T>
1.同样的:
public static void main(String[] args) { List<? super Apple> appleList = new ArrayList<>(); appleList.add(new Apple()); appleList.add(new RedApple()); appleList.add(null); //报错,无法通过编译 appleList.add(new TropicalFruit()); }
2.还是一样的问题,既然规定存放的是Apple以及它的超类,那么TropicalFruit是符合要求的,为什么却无法添加?
3.类比上届通配符,<? super T>集合中保存的是T以及T的父类,编译器是不知道具体的类型的(有可能是直接父类,有可能是超类),有可能是List、List。例子中的代码
List<? super Apple> 存放的是Apple,那么Apple,RedApple都是可以添加的
这样可以添加的原因,想想多态的概念,向上转型是安全的操作(子类的对象赋值给父类的引用),存放的Apple可以向上转型为TropicalFruit、Fruit,Object,如果存入Fruit显然涉及向下强制类型转化,可是编译器判断不了,因为可能是List或者List,也即下界其实定义最小能添加的元素类型T或者T的子类(向上转型属于安全操作)。
4.extends可用于的返回类型限定,不能用于参数类型限定。super可用于参数类型限定,不能用于返回类型限定。
PECS原则
1.如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends->PE)。
2.如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super->CS)。
3.如果既要存又要取,那么就不要使用任何通配符。
你还在原价购买阿里云、腾讯云、华为云、天翼云产品?那就亏大啦!现在申请成为四大品牌云厂商VIP用户,可以3折优惠价购买云服务器等云产品,并且可享四大云服务商产品终身VIP优惠价,还等什么?赶紧点击下面对应链接免费申请VIP客户吧:
相关文章
- Java 多线程(七):线程池
- Java 多线程(五):锁(三)
- Java 多线程(四):锁(二)
- Java 多线程(三):锁(一)
- Java 多线程(二):并发编程的三大特性
- Java 多线程(一):基础
- Java SE 18 新增特性
- Java SE 17 新增特性
- Java SE 16 新增特性
- Java SE 15 新增特性
- Java SE 14 新增特性
- Java SE 10 Application Class-Data Sharing 示例
- Java SE 13 新增特性
- Java SE 12 新增特性
- Java SE 11 新增特性
- Java SE 10 新增特性
- Java SE 9 模块化示例
- Java SE 9 多版本兼容 JAR 包示例
- Java SE 9 新增特性
- Java SE 8 新增特性