理解Java泛型通配符上下边界

<? extends T>和<? super T>是Java泛型中的“通配符(Wildcards)”和“边界(Bounds)”的概念。

  • <? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”
  • <? super T>:是指 “下界通配符(Lower Bounds Wildcards)”
	// 定义一个Number类型的泛型容器
    List<Number> rawNumberContainer = new ArrayList<>();
    // 定义一个Integer类型的泛型容器
    List<Integer> rawIntegerContainer = new ArrayList<>();
    rawIntegerContainer.add(new Integer(1));
    // 定义一个BigDecimal类型的泛型容器
    List<BigDecimal> rawBigDecimalContainer = new ArrayList<>();
    rawBigDecimalContainer.add(BigDecimal.valueOf(1));
    // 定义一个Object类型的泛型容器
    List<Object> rawObjectContainer = new ArrayList<>();

    /*
     * ====================== 泛型上界示例 ======================
     */

    // 声明泛型上界为Number的容器,所有Number及子类型的泛型容器均可以赋值给它,用来提供数据
    List<? extends Number> upperBoundContainer;
    upperBoundContainer = rawNumberContainer;
    // 将Integer容器赋值给Number泛型上界容器 用来提供数据(Producer)
    upperBoundContainer = rawIntegerContainer;
    Number integerAsSuperType = upperBoundContainer.get(0);
    assertTrue(integerAsSuperType instanceof Integer);

    // 将BigDecimal容器赋值给Number泛型上界容器 用来提供数据(Producer)
    upperBoundContainer = rawBigDecimalContainer;
    Number bigDecimalAsUpperType = upperBoundContainer.get(0);
    assertTrue(bigDecimalAsUpperType instanceof BigDecimal);

    /*
     Number泛型上界容器 用来接收数据(Consumer) 会编译报错,因为编译器无法确认
     当前容器是属于BigDecimal还是Integer的容器,如果将BigDecimal容器里添加一个Integer元素类型就不安全了。
     */
    // upperBoundContainer.add(new Integer(1));

    /*
     * ====================== 泛型下界示例 ======================
     */

    // 声明泛型下界为Number的容器,所有Number及其父类型的泛型容器都可以赋值给它,用来接收数据
    List<? super Number> lowerBoundContainer;
    // 将Number类型的泛型容器,赋值给它
    lowerBoundContainer = rawNumberContainer;
    // 可以添加Number及其子类型元素进来
    lowerBoundContainer.add(new Integer(1));
    // 将Object类型的泛型容器,赋值给它
    lowerBoundContainer = rawObjectContainer;
   /*
     虽然rawObjectContainer是可以添加new Object()的,但是将它指定给泛型下界为Number的容器来操作,
     就限制了通过它来添加的数据,只能是Number及其子类型的数据,而Object本身是Number的父类型,那么
     给父类型的泛型容器里添加其子类型或子子类型也是类型安全的了。
    */
    // lowerBoundContainer.add(new Object());
    lowerBoundContainer.add(new Integer(1));
    lowerBoundContainer.add(BigDecimal.valueOf(1));

    /*
     编译报错,因为通过泛型下界为Number的容器能够添加Number及其它子类型元素,而Integer容器只能添
     加Integer及其子类型元素
     */
    //lowerBoundContainer = rawIntegerContainer;


    /*
     * ====================== 如果不使用泛型边界 ======================
     */
    /*
     不管是将父类型容器给赋值给子类型,还是将子类型赋值给夫类型,都会导致编译报错,因为List<Number>和List<Integer>
     不管谁赋值给谁,都只能满足一半的类型安全(List<Integer> 赋值给 List<Number> 能够保证提供的数据类型安全,
     List<Number> 赋值给 List<Integer>能保证接收的数据类型安全)。而通过上下边界来分别满足一个方向的类型安全,可以避免
     产生歧义。
     */
    // rawNumberContainer = rawIntegerContainer;
    // rawIntegerContainer = rawNumberContainer;

泛型上下边界 PECS法则 Producer-Extends-Consumer-Super 提供数据使用上界(extends) 接收数据使用下界(super)

总结:

通过泛型上下界可以用于实现在泛型场景(Java集合容器、泛型类/接口),抽取封装公共逻辑。
例如:
  使用泛型上边界用于定义通过使用Java集合容器来提供数据的参数类型(提供数据)。
  使用泛型下边界用于定义通过使用Java集合容器来接收数据的参数类型(收集数据)。

工具类java.util.Collections#copy的典型应用(既用到了下边界又用到了上边界):

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值