Java 泛型通配符上下界理解及应用

一.通配符的应用

实践检验真理,所以在说明通配符上下界的理解的时候,先说明下什么时候使用通配符

关于泛型
类型通配符的作用是为了代替泛型类的类型实参

当我们使用泛型类作为参数时,我们不想固定具体的泛型类型实参,而是想接收任意类型,或者某个类及其子类或超类类型作为类型实参,这个时候就需要使用通配符了,使用泛型类<?>作为类型实参
总结来说通配符解决的问题就是:类B是类A子类,但是泛型类<B>不是泛型类<A>的子类,但是我们又想有这么个类型能同时接收泛型类<B>泛型类<A>类型作为参数的问题

通配符类型

  • 无边界通配符:<?>
    使用无边界通配符可以让泛型接收任意类型的数据

  • 上边界通配符 :<?extends 具体类型 >
    使用固定上边界的通配符的泛型可以接收指定类型及其所有子类类型的数据,这里的指定类型可以是类也可以是接口

  • 下边界通配符 :<? spuer 具体类型>
    所有固定下边界的通配符的泛型可以接收指定类型及其所有超类类型的数据。

通配符无法同时指定上下边界

举个栗子
我们常用的List就是一个泛型类,以java.lang.Number类及其子类,超类作为类型实参,具体继承关系如下:

2989495-53da111058ee88a0.png

看下下面这段代码:

private List<? extends Number> data;

    private void test() {
        List<Number> numbers = new ArrayList<>();
        List<Integer> integers = new ArrayList<>();
        //List<Number> data = integers; //编译报错
        data = numbers;
        data = integers;
    }

显然虽然IntegerNumber的子类,但是List<Integer>并不是List<Number>的子类,所以List<Integer>类型的变量不能直接赋值给List<Number>的变量。这时候通配符就起到作用了List<? extends Number>可以看做是List<Integer>List<Number>的子类,它可以接收Number类或者其子类型作为类型形参的泛型数据。
所以当我们要在一个方法或者类中接收不固定类型实参的泛型数据,可以考虑使用通配符

二.关于通配符的上下界

1. 无边界 和 上边界通配符

使用无边界和上边界通配符的泛型不能赋值(除了null),可以取值,但是只能去指定的类型及其超类类型(无边界只能取Object类型数据)

(无边界其实上边界就是Object)

List为例:下面这段代码当我们用List<?>或者List<? extends Number>add数据时发现都会编译报错。
为什么:根据上面应用的结论,List<Integer>List<Long>List<Double>这些类型可以理解为List<?>或者List<? extends Number>的子类型。
这时候List<?>或者List<? extends Number> add时候不知道到底要往List<Integer>List<Long>List<Double>还是其他Number子类型的Listadd数据的是IntegerLong还是Double类型。这么操作可能会引发类型不一致的问题,这显然和泛型的设计是相悖的。因此Java为了保证类型一致,是不允许这么操作的。但是null是所有引用类型都有元素,所有可以add成功。

List<?> data = new ArrayList<>();
List<?> data = new ArrayList<>();
data.add(new Object()); //编译报错
data.add(10);  //编译报错
data.add(null);

 List<? extends Number> numbers = new ArrayList<>();
numbers.add(10); //编译报错
numbers.add(new BigDecimal(20)); //编译报错
numbers.add(null);

在看一下get取值方法:
下面代码可以看出(忽略运行错误啊,只是为了说明泛型编译问题):get方法是可以取到指定类型及其超类型的数据。

List<?> data = new ArrayList<>();
Object object = data.get(0);

List<? extends Number> numbers = new ArrayList<>();
Number number = numbers.get(0);
Object number2 = numbers.get(0);
2.下边界通配符

与无边界和上边界通配符相反,下边界通配符只能取Object类型的数据,但可以赋值,只要是指定类型或者其子类型都能成功赋值

还是以List类为例,代码如下,对应到List里面就是add指定类型及其子类型数据时可以正常编译通过,但是get方法不能编译通过
为什么:因为LongBigDecimalFloat都是Numer的子类,根据之前的结论? super Number代表可以接收指定类型及其父类型的数据,所以List<? super Number>可以理解为List<Number>或者List<Object>的父类型,显然List<Number>或者List<Object>是可以add Numer的子类型数据的。但是get的时候因为不知道具体是
List<Number>还是List<Object>或者是之间的什么类型,所以只能get所以类型的父类型Object`类型

List<? super Number> data = new ArrayList<>();
data.add(10);
data.add(new BigDecimal(1000));
data.add(10.07f);
data.add(new Object()) //编译报错

Object object = data.get(0);
Number number = data.get(0); //编译报错

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值