下面我總結了集合、泛型、數組轉集合等一些常見的陷進,認真看完,相信你絕對有所收穫。javascript
說實話,我敢保證不少人是不知道 List, List<?> 與 List<Object> 之間的區別的。java
一、咱們先來看看 List 與 List<Object>數組
不少可能以爲 List<Object>的用法與 List 是同樣的,例如不少人認爲安全
List<Object> list;markdown
與async
List list;ide
這兩種定義方法是如出一轍的,然而他們是不同的。看下面一段代碼post
List<Integer> t1 = new ArrayList<>(); // 編譯經過 List t2 = t1; //編譯失敗 List<Object> t3 = t1;
t1 能夠賦給 t2, 可是 t1 不能賦給 t3,會拋出以下異常ui
從這裏能夠看出google
List list;
與
List<Object> list;
是有區別的,List 變量能夠接受任何泛型的變量,而 List 則不能夠。 二、咱們在看看 Lis<?> 有什麼須要注意的地方: 看下面一段代碼: List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 編譯經過 t2.remove(0); t2.clear(); // 編譯不經過 t2.add(new Object()); List<?> 是一個泛型,在沒有賦值以前,是能夠接受任何集合的賦值的,我想這點你們都知道,可是請注意,賦值以後就不能往裏面添加元素了,提示以下錯誤: 因此 List<?> 通常用來做爲參數來接受外部的集合,或者返回一個不知道具體元素的集合。 List 與 List<?>, List<Object> 的細微區別知道了吧? 二、<? extends T> 與 <? super T>你真的懂嗎? 咱們知道泛型 List<T> 只能放置一種類型,若是你採用 List<Object> 來放置多種類型,而後再進行類型強制轉換的話,那會失去了泛型的初衷。 爲了可以放置多種類型,因而有了 <? extend T> 與 <? super T>,下面先說一些你可能本來就知道的知識: 一、對於 <? extends T> a,a 這個變量能夠接受 T 及其 T 子類的集合,上界爲 T,而且從 a 取出來的類型都會被強制轉換爲 T。重點看下面一個例子: 注意:咱們先約定 Cat(貓) 繼承自 Animal(動物),RedCat(黑貓) 繼承自 Cat 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>,它和 <? extends T> 有點相反。對於 <? super T> a,a 這個變量能夠接受 T 及其 T 父類的集合,下界爲 T,而且從 a 取出來的類型都會被強制轉換爲 Object。重點看下面一個例子: 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 的子類。 總結 一、List<? extends T> a ,能夠把 a 及其 a 的子類賦給 a,從 a 裏取的元素都會被強制轉換爲 T 類型,不過須要注意的是,不能向 a 添加任何除 null 外是元素。 二、List<? super T> a ,能夠把 a 及其 a 的父類賦給 a,從 a 裏取的元素都會被強制轉換爲 Object 類型,不過須要注意的是,能夠向 a 添加元素,但添加的只能是 T 及其子類元素。 三、泛型與重載 咱們先來看一道題,你以爲下面這道題可以編譯經過嗎? public 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"); } } 答是編譯不經過。 兩個方法的參數不一樣,爲何會重載不經過呢? 實際上在 Java 的泛型中,泛型只存在於源碼中,在編譯後的字節碼中,泛型已經被替換爲原生類型了,而且在相應的地方插入了強制轉換的代碼。爲了方便理解,能夠看下面的一段代碼例子: // 源碼 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); } 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換的代碼,編譯以後,咱們反編譯的代碼以下: // 反編譯以後的代碼 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); } 這種 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換代碼的機制咱們也稱之爲擦除。 因此上面的兩個方法,看似參數不同,可是通過編譯擦出以後,他們的參數就是同樣的了,因此編譯不經過。 四、數組與集合相互轉換時須要注意的點 一、數組轉集合 你們先看一個例子吧, public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:編譯正常,但運行時拋出了異常 list.add("four"); } 向集合添加元素拋出了以下異常: 問題來了,向集合添加元素爲啥會拋出異常呢?? 咱們先來看一下 Arrays.asList(arr) 方法究竟返回了什麼? 源碼以下: 返回的明明是 ArrayList 啊,爲啥就不能添加元素呢?? 實際上,此 ArrayList 非彼 ArrayList,這個返回的 ArrayList 其實是 Arrays 的一個內部類。該內部類也是十分簡單,和真實的那個 ArrayList 沒得比,部分源碼以下: 並且這個假的 ArrayList 是直接 引用原數組的,否則你看它的構造器(第二條畫線) 。 也就是說,ArrayList 內部是直接引用 arr 數組,你對 arr 數組進行改變,也會同時改變到 list 集合。 下面的代碼證實這一點 public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); } 打印結果是 「0」。 因此,咱們向 list 添加元素確定失敗,由於 arr 數組的長度了 3 ,原本就有 3 個元素了,你在向裏面添加第四個元素,確定是不行的。 因此,在把數組轉換爲集合的過程當中,須要特別注意。 建議你們這樣轉換比較安全 List<String> list = new ArrayList<>(Arrays.asList(arr)); 二、集合轉數組 集合轉換爲數組相對比較不苛刻,我就不拉不少源碼來進行分析了,我只簡單說下幾個須要注意的地方。例如對於下面這個轉換: // 集合大小爲 size List<String> list = new ArrayList<>(); // 長度爲 n 的數組 String[] arr = new String[n]; // 進行轉換 list.toArray(arr); 一、若是數組長度比集合小:因爲 arr 的長度不夠,因此集合裏的元素不會賦給 arr,並且本身再從新建立一個新數組反回去。 二、若是數組長度不小於集合:此時 arr 的長度夠了,因此集合裏的元素直接複製給 arr 數組,不會從新建立一個新的元素。 一覽源碼: public <T> T[] toArray(T[] a) { if (a.length < size) // 從新建立一個數組來返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 長度夠的話直接複製給 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 以上這些陷進相信有很多人是不知道了,我把它總結整理了出來,但願你們看完可以有所收穫。 下面我總結了集合、泛型、數組轉集合等一些常見的陷進,認真看完,相信你絕對有所收穫。 一、List ,List<?> 與 List<Object> 有區別嗎? 說實話,我敢保證不少人是不知道 List, List<?> 與 List<Object> 之間的區別的。 一、咱們先來看看 List 與 List<Object> 不少可能以爲 List<Object>的用法與 List 是同樣的,例如不少人認爲 List<Object> list; 與 List list; 這兩種定義方法是如出一轍的,然而他們是不同的。看下面一段代碼 List<Integer> t1 = new ArrayList<>(); // 編譯經過 List t2 = t1; //編譯失敗 List<Object> t3 = t1; t1 能夠賦給 t2, 可是 t1 不能賦給 t3,會拋出以下異常 從這裏能夠看出 List list; 與 List<Object> list; 是有區別的,List 變量能夠接受任何泛型的變量,而 List 則不能夠。 二、咱們在看看 Lis<?> 有什麼須要注意的地方: 看下面一段代碼: List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 編譯經過 t2.remove(0); t2.clear(); // 編譯不經過 t2.add(new Object()); List<?> 是一個泛型,在沒有賦值以前,是能夠接受任何集合的賦值的,我想這點你們都知道,可是請注意,賦值以後就不能往裏面添加元素了,提示以下錯誤: 因此 List<?> 通常用來做爲參數來接受外部的集合,或者返回一個不知道具體元素的集合。 List 與 List<?>, List<Object> 的細微區別知道了吧? 二、<? extends T> 與 <? super T>你真的懂嗎? 咱們知道泛型 List<T> 只能放置一種類型,若是你採用 List<Object> 來放置多種類型,而後再進行類型強制轉換的話,那會失去了泛型的初衷。 爲了可以放置多種類型,因而有了 <? extend T> 與 <? super T>,下面先說一些你可能本來就知道的知識: 一、對於 <? extends T> a,a 這個變量能夠接受 T 及其 T 子類的集合,上界爲 T,而且從 a 取出來的類型都會被強制轉換爲 T。重點看下面一個例子: 注意:咱們先約定 Cat(貓) 繼承自 Animal(動物),RedCat(黑貓) 繼承自 Cat 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>,它和 <? extends T> 有點相反。對於 <? super T> a,a 這個變量能夠接受 T 及其 T 父類的集合,下界爲 T,而且從 a 取出來的類型都會被強制轉換爲 Object。重點看下面一個例子: 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 的子類。 總結 一、List<? extends T> a ,能夠把 a 及其 a 的子類賦給 a,從 a 裏取的元素都會被強制轉換爲 T 類型,不過須要注意的是,不能向 a 添加任何除 null 外是元素。 二、List<? super T> a ,能夠把 a 及其 a 的父類賦給 a,從 a 裏取的元素都會被強制轉換爲 Object 類型,不過須要注意的是,能夠向 a 添加元素,但添加的只能是 T 及其子類元素。 三、泛型與重載 咱們先來看一道題,你以爲下面這道題可以編譯經過嗎? public 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"); } } 答是編譯不經過。 兩個方法的參數不一樣,爲何會重載不經過呢? 實際上在 Java 的泛型中,泛型只存在於源碼中,在編譯後的字節碼中,泛型已經被替換爲原生類型了,而且在相應的地方插入了強制轉換的代碼。爲了方便理解,能夠看下面的一段代碼例子: // 源碼 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); } 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換的代碼,編譯以後,咱們反編譯的代碼以下: // 反編譯以後的代碼 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); } 這種 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換代碼的機制咱們也稱之爲擦除。 因此上面的兩個方法,看似參數不同,可是通過編譯擦出以後,他們的參數就是同樣的了,因此編譯不經過。 四、數組與集合相互轉換時須要注意的點 一、數組轉集合 你們先看一個例子吧, public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:編譯正常,但運行時拋出了異常 list.add("four"); } 向集合添加元素拋出了以下異常: 問題來了,向集合添加元素爲啥會拋出異常呢?? 咱們先來看一下 Arrays.asList(arr) 方法究竟返回了什麼? 源碼以下: 返回的明明是 ArrayList 啊,爲啥就不能添加元素呢?? 實際上,此 ArrayList 非彼 ArrayList,這個返回的 ArrayList 其實是 Arrays 的一個內部類。該內部類也是十分簡單,和真實的那個 ArrayList 沒得比,部分源碼以下: 並且這個假的 ArrayList 是直接 引用原數組的,否則你看它的構造器(第二條畫線) 。 也就是說,ArrayList 內部是直接引用 arr 數組,你對 arr 數組進行改變,也會同時改變到 list 集合。 下面的代碼證實這一點 public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); } 打印結果是 「0」。 因此,咱們向 list 添加元素確定失敗,由於 arr 數組的長度了 3 ,原本就有 3 個元素了,你在向裏面添加第四個元素,確定是不行的。 因此,在把數組轉換爲集合的過程當中,須要特別注意。 建議你們這樣轉換比較安全 List<String> list = new ArrayList<>(Arrays.asList(arr)); 二、集合轉數組 集合轉換爲數組相對比較不苛刻,我就不拉不少源碼來進行分析了,我只簡單說下幾個須要注意的地方。例如對於下面這個轉換: // 集合大小爲 size List<String> list = new ArrayList<>(); // 長度爲 n 的數組 String[] arr = new String[n]; // 進行轉換 list.toArray(arr); 一、若是數組長度比集合小:因爲 arr 的長度不夠,因此集合裏的元素不會賦給 arr,並且本身再從新建立一個新數組反回去。 二、若是數組長度不小於集合:此時 arr 的長度夠了,因此集合裏的元素直接複製給 arr 數組,不會從新建立一個新的元素。 一覽源碼: public <T> T[] toArray(T[] a) { if (a.length < size) // 從新建立一個數組來返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 長度夠的話直接複製給 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 以上這些陷進相信有很多人是不知道了,我把它總結整理了出來,若是你們看完以爲有收穫,不妨點個底部小卡片 + 點贊鼓勵我一下? posted @ 2019-03-01 16:47 帥地 閱讀( ...) 評論( ...) 編輯 收藏 刷新評論 刷新頁面 返回頂部
二、咱們在看看 Lis<?> 有什麼須要注意的地方:
看下面一段代碼:
List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 編譯經過 t2.remove(0); t2.clear(); // 編譯不經過 t2.add(new Object());
List<?> 是一個泛型,在沒有賦值以前,是能夠接受任何集合的賦值的,我想這點你們都知道,可是請注意,賦值以後就不能往裏面添加元素了,提示以下錯誤:
因此 List<?> 通常用來做爲參數來接受外部的集合,或者返回一個不知道具體元素的集合。
List 與 List<?>, List<Object> 的細微區別知道了吧?
咱們知道泛型 List<T> 只能放置一種類型,若是你採用 List<Object> 來放置多種類型,而後再進行類型強制轉換的話,那會失去了泛型的初衷。
爲了可以放置多種類型,因而有了 <? extend T> 與 <? super T>,下面先說一些你可能本來就知道的知識:
一、對於 <? extends T> a,a 這個變量能夠接受 T 及其 T 子類的集合,上界爲 T,而且從 a 取出來的類型都會被強制轉換爲 T。重點看下面一個例子:
注意:咱們先約定 Cat(貓) 繼承自 Animal(動物),RedCat(黑貓) 繼承自 Cat
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>,它和 <? extends T> 有點相反。對於 <? super T> a,a 這個變量能夠接受 T 及其 T 父類的集合,下界爲 T,而且從 a 取出來的類型都會被強制轉換爲 Object。重點看下面一個例子:
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 的子類。
總結
一、List<? extends T> a ,能夠把 a 及其 a 的子類賦給 a,從 a 裏取的元素都會被強制轉換爲 T 類型,不過須要注意的是,不能向 a 添加任何除 null 外是元素。
二、List<? super T> a ,能夠把 a 及其 a 的父類賦給 a,從 a 裏取的元素都會被強制轉換爲 Object 類型,不過須要注意的是,能夠向 a 添加元素,但添加的只能是 T 及其子類元素。
咱們先來看一道題,你以爲下面這道題可以編譯經過嗎?
public 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"); } }
答是編譯不經過。
兩個方法的參數不一樣,爲何會重載不經過呢?
實際上在 Java 的泛型中,泛型只存在於源碼中,在編譯後的字節碼中,泛型已經被替換爲原生類型了,而且在相應的地方插入了強制轉換的代碼。爲了方便理解,能夠看下面的一段代碼例子:
// 源碼 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); }
編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換的代碼,編譯以後,咱們反編譯的代碼以下:
// 反編譯以後的代碼 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); }
這種 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換代碼的機制咱們也稱之爲擦除。
因此上面的兩個方法,看似參數不同,可是通過編譯擦出以後,他們的參數就是同樣的了,因此編譯不經過。
一、數組轉集合
你們先看一個例子吧,
public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:編譯正常,但運行時拋出了異常 list.add("four"); }
向集合添加元素拋出了以下異常:
問題來了,向集合添加元素爲啥會拋出異常呢??
咱們先來看一下 Arrays.asList(arr) 方法究竟返回了什麼?
源碼以下:
返回的明明是 ArrayList 啊,爲啥就不能添加元素呢??
實際上,此 ArrayList 非彼 ArrayList,這個返回的 ArrayList 其實是 Arrays 的一個內部類。該內部類也是十分簡單,和真實的那個 ArrayList 沒得比,部分源碼以下:
並且這個假的 ArrayList 是直接 引用原數組的,否則你看它的構造器(第二條畫線)
。
也就是說,ArrayList 內部是直接引用 arr 數組,你對 arr 數組進行改變,也會同時改變到 list 集合。
下面的代碼證實這一點
public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); }
打印結果是 「0」。
因此,咱們向 list 添加元素確定失敗,由於 arr 數組的長度了 3 ,原本就有 3 個元素了,你在向裏面添加第四個元素,確定是不行的。
因此,在把數組轉換爲集合的過程當中,須要特別注意。
建議你們這樣轉換比較安全
List<String> list = new ArrayList<>(Arrays.asList(arr));
二、集合轉數組
集合轉換爲數組相對比較不苛刻,我就不拉不少源碼來進行分析了,我只簡單說下幾個須要注意的地方。例如對於下面這個轉換:
// 集合大小爲 size List<String> list = new ArrayList<>(); // 長度爲 n 的數組 String[] arr = new String[n]; // 進行轉換 list.toArray(arr);
一、若是數組長度比集合小:因爲 arr 的長度不夠,因此集合裏的元素不會賦給 arr,並且本身再從新建立一個新數組反回去。
二、若是數組長度不小於集合:此時 arr 的長度夠了,因此集合裏的元素直接複製給 arr 數組,不會從新建立一個新的元素。
一覽源碼:
public <T> T[] toArray(T[] a) { if (a.length < size) // 從新建立一個數組來返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 長度夠的話直接複製給 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
以上這些陷進相信有很多人是不知道了,我把它總結整理了出來,但願你們看完可以有所收穫。
下面我總結了集合、泛型、數組轉集合等一些常見的陷進,認真看完,相信你絕對有所收穫。
說實話,我敢保證不少人是不知道 List, List<?> 與 List<Object> 之間的區別的。
一、咱們先來看看 List 與 List<Object>
不少可能以爲 List<Object>的用法與 List 是同樣的,例如不少人認爲
這兩種定義方法是如出一轍的,然而他們是不同的。看下面一段代碼
t1 能夠賦給 t2, 可是 t1 不能賦給 t3,會拋出以下異常
從這裏能夠看出
是有區別的,List 變量能夠接受任何泛型的變量,而 List 則不能夠。 二、咱們在看看 Lis<?> 有什麼須要注意的地方: 看下面一段代碼: List<Object> t1 = new ArrayList<>(); List<?> t2 = t1; // 編譯經過 t2.remove(0); t2.clear(); // 編譯不經過 t2.add(new Object()); List<?> 是一個泛型,在沒有賦值以前,是能夠接受任何集合的賦值的,我想這點你們都知道,可是請注意,賦值以後就不能往裏面添加元素了,提示以下錯誤: 因此 List<?> 通常用來做爲參數來接受外部的集合,或者返回一個不知道具體元素的集合。 List 與 List<?>, List<Object> 的細微區別知道了吧? 二、<? extends T> 與 <? super T>你真的懂嗎? 咱們知道泛型 List<T> 只能放置一種類型,若是你採用 List<Object> 來放置多種類型,而後再進行類型強制轉換的話,那會失去了泛型的初衷。 爲了可以放置多種類型,因而有了 <? extend T> 與 <? super T>,下面先說一些你可能本來就知道的知識: 一、對於 <? extends T> a,a 這個變量能夠接受 T 及其 T 子類的集合,上界爲 T,而且從 a 取出來的類型都會被強制轉換爲 T。重點看下面一個例子: 注意:咱們先約定 Cat(貓) 繼承自 Animal(動物),RedCat(黑貓) 繼承自 Cat 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>,它和 <? extends T> 有點相反。對於 <? super T> a,a 這個變量能夠接受 T 及其 T 父類的集合,下界爲 T,而且從 a 取出來的類型都會被強制轉換爲 Object。重點看下面一個例子: 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 的子類。 總結 一、List<? extends T> a ,能夠把 a 及其 a 的子類賦給 a,從 a 裏取的元素都會被強制轉換爲 T 類型,不過須要注意的是,不能向 a 添加任何除 null 外是元素。 二、List<? super T> a ,能夠把 a 及其 a 的父類賦給 a,從 a 裏取的元素都會被強制轉換爲 Object 類型,不過須要注意的是,能夠向 a 添加元素,但添加的只能是 T 及其子類元素。 三、泛型與重載 咱們先來看一道題,你以爲下面這道題可以編譯經過嗎? public 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"); } } 答是編譯不經過。 兩個方法的參數不一樣,爲何會重載不經過呢? 實際上在 Java 的泛型中,泛型只存在於源碼中,在編譯後的字節碼中,泛型已經被替換爲原生類型了,而且在相應的地方插入了強制轉換的代碼。爲了方便理解,能夠看下面的一段代碼例子: // 源碼 public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); System.out.println(list.get(0)); } 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換的代碼,編譯以後,咱們反編譯的代碼以下: // 反編譯以後的代碼 public static void main(String[] args) { List list = new ArrayList(); list.add(1); System.out.println((Integer)list.get(0)); } 這種 編譯以後泛型就不存在了,而且在相應的地方插入了強制轉換代碼的機制咱們也稱之爲擦除。 因此上面的兩個方法,看似參數不同,可是通過編譯擦出以後,他們的參數就是同樣的了,因此編譯不經過。 四、數組與集合相互轉換時須要注意的點 一、數組轉集合 你們先看一個例子吧, public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 向集合添加元素:編譯正常,但運行時拋出了異常 list.add("four"); } 向集合添加元素拋出了以下異常: 問題來了,向集合添加元素爲啥會拋出異常呢?? 咱們先來看一下 Arrays.asList(arr) 方法究竟返回了什麼? 源碼以下: 返回的明明是 ArrayList 啊,爲啥就不能添加元素呢?? 實際上,此 ArrayList 非彼 ArrayList,這個返回的 ArrayList 其實是 Arrays 的一個內部類。該內部類也是十分簡單,和真實的那個 ArrayList 沒得比,部分源碼以下: 並且這個假的 ArrayList 是直接 引用原數組的,否則你看它的構造器(第二條畫線) 。 也就是說,ArrayList 內部是直接引用 arr 數組,你對 arr 數組進行改變,也會同時改變到 list 集合。 下面的代碼證實這一點 public static void main(String[] args) { String[] arr = {"one", "two", "three"}; // 數組轉換成集合 List<String> list = Arrays.asList(arr); // 修改 arr arr[0] = "0"; //打印看看 System.out.println(list.get(0)); } 打印結果是 「0」。 因此,咱們向 list 添加元素確定失敗,由於 arr 數組的長度了 3 ,原本就有 3 個元素了,你在向裏面添加第四個元素,確定是不行的。 因此,在把數組轉換爲集合的過程當中,須要特別注意。 建議你們這樣轉換比較安全 List<String> list = new ArrayList<>(Arrays.asList(arr)); 二、集合轉數組 集合轉換爲數組相對比較不苛刻,我就不拉不少源碼來進行分析了,我只簡單說下幾個須要注意的地方。例如對於下面這個轉換: // 集合大小爲 size List<String> list = new ArrayList<>(); // 長度爲 n 的數組 String[] arr = new String[n]; // 進行轉換 list.toArray(arr); 一、若是數組長度比集合小:因爲 arr 的長度不夠,因此集合裏的元素不會賦給 arr,並且本身再從新建立一個新數組反回去。 二、若是數組長度不小於集合:此時 arr 的長度夠了,因此集合裏的元素直接複製給 arr 數組,不會從新建立一個新的元素。 一覽源碼: public <T> T[] toArray(T[] a) { if (a.length < size) // 從新建立一個數組來返回去 return (T[]) Arrays.copyOf(elementData, size, a.getClass()); // 長度夠的話直接複製給 a System.arraycopy(elementData, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } 以上這些陷進相信有很多人是不知道了,我把它總結整理了出來,若是你們看完以爲有收穫,不妨點個底部小卡片 + 點贊鼓勵我一下? posted @ 2019-03-01 16:47 帥地 閱讀( ...) 評論( ...) 編輯 收藏 刷新評論 刷新頁面 返回頂部
以上這些陷進相信有很多人是不知道了,我把它總結整理了出來,若是你們看完以爲有收穫,不妨點個底部小卡片 + 點贊鼓勵我一下?