12.Java 基础 - 通配符

来源:互联网 发布:三国演义怀旧剧场源码 编辑:程序博客网 时间:2024/04/28 12:33

基本概念

通配符(Wildcard) ,使用一个问号表示类型参数 , 是一种表示【未知类型】的【类型约束】的方法

泛型将数据类型定义为一个参数,我们可以在用到的使用再指定具体的类型。但是如果在用到的时候还不能确定具体的类型,就需要依靠通配符来解决。

抽象的讲,因为泛型不支持协变,所以才引入了通配符,使得将泛型类型变成协变的。


通配符作用

下面通过两个例子来探究下通配符的作用。

1.不使用通配符

ArrayList<Object> list_obj = null;ArrayList<String> list_str = new ArrayList<String>();// ArrayList 的元素支持  String -> Object 的强制转换list_str.add("hello");System.out.println((Object)list_str.get(0));// 编译错误,泛型不支持协变list_obj = list_str;

观察代码,发现:

  • 存放在 ArrayList 中的元素却是支持从 String -> Object 的向上转型,因为 String 是 Object 的子类。

  • 当 ArrayList 的参数类型从 String -> Object 转换的时候出现了编译错误,因为泛型不支持协变。


2.使用通配符

ArrayList<String> list_str = new ArrayList<String>();list_str.add("hello");ArrayList<Integer> list_int = new ArrayList<Integer>();list_int.add(100);ArrayList<?> list = null;list = list_str;System.out.println(list.get(0));    // hellolist = list_int;System.out.println(list.get(0));    // 100//编译错误list.add("hello");list.add(100);

观察代码,发现:

  • 在使用了通配符之后,它允许其接受任意类型的转变,正好解决了泛型不支持协变的限制。

  • 但是同时也失去了向其中加入任何类型对象的权利。


通配符类型

通配符的形式有三种,分别是:

//1、无限定通配符 Demo<?>//2、上边界限定通配符 Demo< ? extends Number>   //3、下边界限定通配符    Demo< ? super Number>  

1.上边界限定通配符

观察下面的例子:定义了三个带有继承关系的类,分别是 Person -> Man -> Worker。

class Person{    void print(){    }}class Man extends Person{    void paly(){    }}class Worker extends Man{    void say(){    }}public class Test {    public static void main(String[] args)  {        // 创建了三个 ArrayList 数组        ArrayList<Person> personList = new ArrayList<Person>();        ArrayList<Man> manList= new ArrayList<Man>();        ArrayList<Worker> workerList = new ArrayList<Worker>();        // 限定了上界, ? 代表 Man 以及继承 Man 的类        ArrayList<? extends Man> list  = null;        list = manList;        list = workerList;        // 编译错误,因为 Person 是 Man 的父类        list = personList;        // 编译错误,因为使用了通配符就不能添加对象        list.add(new Man());        list.add(new Worker());        for(Man man : list){            man.print();            man.paly();        }        for(Person per : list){            per.print();        }        // 编译错误,因为 ? 也可以代表 Man        // 从继承我们可以知道 Worker 肯定是 Man,但 Man 不一定是 Worker。        // for(Worker worker : list);    }}

2.下边界限定通配符

// 省略相同代码...public class Test {    public static void main(String[] args)  {        ArrayList<Person> personList = new ArrayList<Person>();        ArrayList<Man> manList= new ArrayList<Man>();        ArrayList<Worker> workList = new ArrayList<Worker>();        // 限定了下届, ? 代表 Man 以及 Man 的父类        ArrayList<? super Man> list  = null;        list = personList;        list = manList;        // 编译错误,因为 Worker 是 Man 的子类        list = workList;        // 编译错误,无法确定其父类对象        // Person 继承子 Cell 类,那么用 Person 遍历就是错误的        // for(Man man : list);        // for(Person per : list);        // 因为 Object 是所有类的根类,所以可以用Object来遍历        // for(Object obj : list);    }}

3.无边界通配符

// 省略相同代码...public class Test {    public static void main(String[] args)  {        ArrayList<Person> personList = new ArrayList<Person>();        ArrayList<Man> manList= new ArrayList<Man>();        ArrayList<Worker> workList = new ArrayList<Worker>();        //无边界通配符        ArrayList<?> list  = null;        //可以代表一切类型        list = personList;        list = manList;        list = workList;        //为了避免类型混淆,只允许使用 Object 遍历        for(Object obj : list);     }}
1 0