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<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> 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>,它和 <? 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 及其子類元素。
你以爲下面這道題可以編譯經過嗎?
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));