zoukankan      html  css  js  c++  java
  • 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集合与泛型中的几个陷阱,你掉进了几个?

  • 相关阅读:
    Spring学习之声明式事物管理
    SSH整合所需jar
    Spring学习之AOP
    常见MyEclipse报错—— serialVersionUID的作用
    Spring学习之基本概念
    Java基础——I/O续
    Java基础——I/O
    Vim学习总结
    Java基础——异常处理
    Git 学习总结
  • 原文地址:https://www.cnblogs.com/hongdada/p/10683795.html
Copyright © 2011-2022 走看看