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集合与泛型中的几个陷阱,你掉进了几个?

posted @ 2019-04-10 15:40  hongdada  阅读(7846)  评论(0编辑  收藏  举报