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));