Java集合與泛型中的陷阱

List,List<Object>區別

List<Integer> t1 = new ArrayList<>();
        // 編譯經過
        List t2 = t1;
        //編譯失敗
        List<Object> t3 = t1;

t1 能夠賦給 t2, 可是 t1 不能賦給 t3,會拋出以下異常html

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<?> 是一個泛型,在沒有賦值以前,是能夠接受任何集合的賦值的,可是請注意,賦值以後就不能往裏面添加元素了java

提示以下錯誤:數組

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 取出來的類型都會被強制轉換爲 Tdom

class Animal{

}

class Cat extends Animal{

}

class RedCat extends Cat{

}

demo:ide

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<?> 有點相似ui

<? super T>

<? super T>,它和 <? extends T> 有點相反。對於 <? super T> a,a 這個變量能夠接受 T 及其 T 父類的集合,下界爲 T,而且從 a 取出來的類型都會被強制轉換爲 Objectcode

demo:htm

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 的子類blog

總結

一、List<? extends T> a ,能夠把 a 及其 a 的子類賦給 a,從 a 裏取的元素都會被強制轉換爲 T 類型,不過須要注意的是,不能向 a 添加任何除 null 外是元素

二、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集合與泛型中的幾個陷阱,你掉進了幾個?

相關文章
相關標籤/搜索