Java集合与泛型中的陷阱

List,List<Object>区别

  List<Integer> t1 = new ArrayList<>();
        // 编译通过
        List t2 = t1;
        //编译失败
        List<Object> t3 = t1;

t1 可以赋给 t2, 但是 t1 不能赋给 t3,会抛出如下异常

Error:(16, 35) java: 不兼容的类型: java.util.List<java.lang.Integer>无法转换为java.util.List<java.lang.Object>

List<?>注意点

    List<Object> t1 = new ArrayList<>();
        List<?> t2 = t1;
        // 编译通过
        t2.remove(0);
        t2.clear();
        // 编译不通过
        t2.add(new Object());

List<?> 是一个泛型,在没有赋值之前,是可以接受任何集合的赋值的,但是请注意,赋值之后就不能往里面添加元素了

提示如下错误:

Error:(18, 19) java: 对于add(java.lang.Object), 找不到合适的方法
    方法 java.util.Collection.add(capture#1, 共 ?)不适用
      (参数不匹配; java.lang.Object无法转换为capture#1, 共 ?)
    方法 java.util.List.add(capture#1, 共 ?)不适用
      (参数不匹配; java.lang.Object无法转换为capture#1, 共 ?)

所以 List<?> 一般用来作为参数来接受外部的集合,或者返回一个不知道具体元素的集合。

<? extends T> 与 <? super T>

<? extends T>

<? extends T> a,a 这个变量可以接受 T 及其 T 子类的集合,上界为 T,并且从 a 取出来的类型都会被强制转换为 T

class Animal{

}

class Cat extends Animal{

}

class RedCat extends Cat{

}

demo:

        List<Animal> animals = new ArrayList<>();
        List<Cat> cats = new ArrayList<>();
        List<RedCat> redCats = new ArrayList<>();
        // 可以通过编译
        List<? extends  Cat> extendsCat = redCats;
        // 不能通过编译,因为只能接受 Cat 及其子类的集合
        extendsCat = animals;

        // 重点注意:下面三行都不能通过编译
        extendsCat.add(new Animal());
        extendsCat.add(new Cat());
        extendsCat.add(new RedCat());
        // 重点注意:可以通过编译
        extendsCat.add(null);

<? extends T>最需要注意的是,就是不能向里面添加除null之外的其他所有元素,这个和 List<?> 有点类似

<? super T>

<? super T>,它和 <? extends T> 有点相反。对于 <? super T> a,a 这个变量可以接受 T 及其 T 父类的集合,下界为 T,并且从 a 取出来的类型都会被强制转换为 Object

demo:

    List<Animal> animals = new ArrayList<>();
    List<Cat> cats = new ArrayList<>();
    List<RedCat> redCats = new ArrayList<>();
    // 可以通过编译
    List<? super  Cat> superCat = animals;
    // 不能通过编译,因为只能接受 Cat 及其父类的集合
    superCat = redCats;

    // 重点注意:不能通过编译,只能添加 Cat 及其 Cat 的子类
    superCat.add(new Animal());
    // 重点注意,可以通过编译
    superCat.add(new Cat());
    superCat.add(new RedCat());
    superCat.add(null);

注意,<? super T>最需要注意的是,在虽然可以接受 T 及其父类的赋值,但是只能向里面添加 T 及其 T 的子类

总结

1、List<? extends T> a ,可以把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过需要注意的是,不能向 a 添加任何除 null 外是元素

2、List<? super T> a ,可以把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过需要注意的是,可以向 a 添加元素,但添加的只能是 T 及其子类元素

List泛型与重载

你觉得下面这道题能够编译通过吗?

class GernerTypes {
    public static void  method(List<Integer> list) {
        System.out.println("List<Integer> list");
    }
    public static void method(List<String> list) {
        System.out.println("List<String> list");
    }
}

不通过编译,错误信息:

Error:(20, 28) java: 名称冲突: method(java.util.List<java.lang.String>)和method(java.util.List<java.lang.Integer>)具有相同疑符

两个方法的参数不同,为什么会重载不通过呢?

实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,并且在相应的地方插入了强制转换的代码。

所以上面的两个方法,看似参数不一样,但是经过编译擦出之后,他们的参数就是一样的了,所以编译不通过。

数组集合转换:

String[] arr = {"one", "two", "three"};
        // 数组转换成集合
        List<String> list = Arrays.asList(arr);
        // 向集合添加元素:编译正常,但运行时抛出了异常
        list.add("four");

运行时报错:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at java.util.AbstractList.add(AbstractList.java:108)
    at com.qhong.basic.list.test.main(test.java:16)

问题来了,向集合添加元素为啥会抛出异常呢??

我们先来看一下 Arrays.asList(arr) 方法究竟返回了什么?

看源码:

 public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }
    。。。。

返回的List内部直接引用了原数组arr

原数组长度固定为3,所以不可以再add

下面的代码证明这一点

String[] arr = {"one", "two", "three"};
        // 数组转换成集合
        List<String> list = Arrays.asList(arr);
        // 修改 arr
        arr[0] = "0";
        //打印看看
        System.out.println(list.get(0));

打印:

0

建议大家这样转换比较安全

List<String> list = new ArrayList<>(Arrays.asList(arr));

参考:

Java集合与泛型中的几个陷阱,你掉进了几个?

    原文作者:hongdada
    原文地址: https://www.cnblogs.com/hongdada/p/10683795.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞