Java泛型之界定通配符(extends 和 super)

界定通配符

协变
    public class ArraysCovariant {
        public static void main(String[] args) {   
            class Animal {}
            class Bird extends Animal {}
            class Dog extends Animal {}
            class Cat extends Animal {}
    
            Animal[] animal = new Animal[20];
            animal[0] = new Bird(); // OK
            animal[1] = new Dog(); // OK
            try {
                animal[0] = new Animal(); //OK
            } catch(Exception e) { 
                System.out.println(e);
            }
            try {
                animal[0] = new Animal(); // OK
            } catch(Exception e) { 
                System.out.println(e);
            }
        }
    }

animal 为数组,每个元素为 Animal,由于子类可以赋值给父类,因此,这里没有任何问题。这就是数组的协变。显然,协变带来了不确定性,让程序无法按照预期运行。

集合不支持协变。

    List<Animal> animal  = new ArrayList<Bird>(); // 报错

为了让泛型在特殊情况下支持协议,因此,出现了界定通配符。

界定通配符
    Vector<? extends 类型1> x = new Vector<类型2>(); //上边界:类型2就只能是类型1或者是类型1的子类
    Vector<? super 类型1> x = new Vector<类型2>();   //下边界:类型2就只能是类型1或者是类型1的父类

一个例子彻底理解界定通配符

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    //一个基本的前提:父类赋值给子类,而子类可以赋值给父类
    public class GenericTest {
        public static void main(String[] args) throws Exception {
            class Animal {}
            class Bird extends Animal {}
            class Dog extends Animal {}
            class Cat extends Animal {}
            
            List<Animal> tmpList = new ArrayList<>();
            tmpList.add(1);
            tmpList.add(2);
            tmpList.add(3);
    
            // extendsList 元素的类型是 Animal 或其子类。
            List<? extends Animal> extendsList = tmpList;
            // add,set 的参数包括泛型,要将 Bird 转换为 Animal 的子类,由于具体子类不清楚,这是错误的
            extendsList.add(new Bird('a'));
            extendsList.set(1, new Bird('b'));
    
            // remove 的参数是 Object,不是泛型,因此没问题
            extendsList.remove(new Bird('a'));
            extendsList.contains(new Bird('b'));
    
            // get 的返回值为泛型 Animal 的子类,可以转换为父类 Animal
            Animal extendsGet = extendsList.get(1);
    
    
            // superList 元素的类型是 Animal 或其父类。
            List<? super Animal> superList = tmpList;
            
            // add,set 的参数包括泛型,要将 Integer 转换为 Number 的父类,这是没问题的
            superList.add(new Bird('a'));
            superList.set(1, new Bird('b'));
    
            // remove, contains 的参数是 Object,不是泛型,因此没问题
            superList.remove(new Bird(1));
            superList.contains(1);
    
            // get 返回值为泛型 Animal 的父类,Animal 的父类不可以转换为 Animal
            Animal superGet = superList.get(1);
    }

特殊情况说明

    //Collections 的 sort 方法
    public class Collections {
        public static <T extends Comparable<? super T>> void sort(List<T> list) {
            list.sort(null);
        }
    }

这里泛型类型是 T

  1. T 要支持排序,必须实现 Comparable 接口,因此 T extends Comparable
  2. 为了 T 及子类能够使用 Comparable 接口,因此 T extends Comparable<? extends T>
通配符 与 T 的区别

T:作用于模板上,用于将数据类型进行参数化,不能用于实例化对象。
?:在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义。

总结

  • 限定通配符总是包括自己
  • 上界类型通配符:参数是泛型的方法受限(很多文章都没有说清楚这里)
  • 下界类型通配符:返回值为泛型的方法受限(很多文章都没有说清楚这里)
  • 如果你想从一个数据类型里获取数据,使用 ? extends 通配符
  • 如果你想把对象写入一个数据结构里,使用 ? super 通配符
  • 如果你既想存,又想取,那就别用通配符
  • 不能同时声明泛型通配符上界和下界
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值