Java中的无界通配符<?>在什么时候使用

本文首发于掘金

ListList 的区别

区别:

  1. List变量的元素只能读,不能写(可以插入null元素)
  2. List不是List的父类,但List是所有List泛型的父类,因此List变量可以被赋值为多种List泛型类型
    public static void printList(List<Object> list) {
        for (Object elem : list)
            System.out.println(elem + " ");
        System.out.println();
    }
    

    上面代码中定义的方法只能接受List类型的变量为参数,不能接受List,改成List的话就可以接受了。

    ListList 的区别

    List是所有List泛型的父类,而List是List泛型的原类型(raw type)。允许使用List原类型是为了兼容引入泛型前的代码,使用原类型会失去泛型的安全性和表达性,可能在运行时报错,还需要自己转型。因此编译器会给出警告。

    注意,有一些需要使用原类型的场景:

    1. 在类的字面量中必须使用原类型,List.classString[].classint.class 是正确的,但 List.class List.class 是错误的。
    2. instanceof 操作符中使用原类型就够了,因为泛型信息会在运行时擦除,在 instanceof 操作符中只能使用无界通配符类型或原类型,而且两者并没有区别,推荐后者。
    // instanceof 操作符的推荐使用方式
    if (o instanceof Set) {
        Set<?> s = (Set<?>) o; // 这种转型时有检查的,不会有编译时警告
    }
    

    的使用场景

    在泛型通配符的in-out使用规则中:

    1. 用来读取的List变量(in),使用extend定义下界;能使用通配符的地方,就改用无界通配符
    2. 用来写入的List变量(out),使用super定义上界
    3. 当List变量同时需要in-out访问时,不使用通配符
    4. 例如JDK源码中List接口的的containsAll、removeAll方法都是用定义的:

      boolean containsAll(Collection<?> c);
      boolean removeAll(Collection<?> c);
      

      而addAll方法,虽然也是读取,但是在后续使用时,会调用E的方法,因此不能使用通配符。

      boolean addAll(Collection<? extends E> c);
      

      参考备注

      1. 参考了Oracle Java文档 Unbounded Wildcards 和 Guidelines for Wildcard Use

      你可能感兴趣的:(Java,java,泛型)