在泛型(Generic type或Generics)出現以前,是這麼寫代碼的:java
public static void main(String[] args) { List list = new ArrayList(); list.add("123"); list.add("456"); System.out.println((String)list.get(0)); }
固然這是徹底容許的,由於List裏面的內容是Object類型的,天然任何對象類型均可以放入、均可以取出,可是這麼寫會有兩個問題:sql
一、當一個對象放入集合時,集合不會記住此對象的類型,當再次從集合中取出此對象時,該對象的編譯類型變成了Object安全
二、運行時須要人爲地強制轉換類型到具體目標,實際的程序毫不會這麼簡單,一個不當心就會出現java.lang.ClassCastException,即類型轉換異常架構
因此,泛型出現以後,上面的代碼就改爲了你們都熟知的寫法:併發
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("123"); list.add("456"); System.out.println(list.get(0)); }
這就是泛型。泛型是對Java語言類型系統的一種擴展,有點相似於C++的模板,能夠把類型參數看做是使用參數化類型時指定的類型的一個佔位符。引入泛型,是對Java語言一個較大的功能加強,帶來了不少的好處:分佈式
一、類型安全。類型錯誤如今在編譯期間就被捕獲到了,而不是在運行時看成java.lang.ClassCastException展現出來,將類型檢查從運行時挪到編譯時有助於開發者更容易找到錯誤,並提升程序的可靠性高併發
二、消除了代碼中許多的強制類型轉換,加強了代碼的可讀性性能
三、爲較大的優化帶來了可能學習
getClass()相同優化
看一段代碼:
public static void main(String[] args) { List<String> stringList = new ArrayList<String>(); List<Integer> integerList = new ArrayList<Integer>(); System.out.println(stringList.getClass() == integerList.getClass()); }
運行結果爲:
true
這意味着,泛型是什麼並不會對一個對象實例是什麼類型的形成影響,因此,經過改變泛型的方式試圖定義不一樣的重載方法也是不能夠的:
儘可能使用精確的類型定義泛型
儘可能使用精確的類型定義泛型,除非必要,不然不要寫一個接口或者父類上去:
public static void main(String[] args) { List<Number> list = new ArrayList<Number>(); list.add(4); list.add(2.2); for (Number number : list) System.out.println(number); }
就像這樣,list中的是一個Number類型,往裏面添加的是Integer與Double,這樣致使get出來的元素也都是Number類型的,失去了子類擴展的功能。若是要讓子類變爲Interger和Double也能夠,(Integer)list.get(0)和(Double)list.get(1)強轉就能夠了,可是這樣不就失去了泛型的意義了嗎?因此,儘可能用精確的類型去定義泛型。
使用類型通配符
List<Object>不是List<String>的父類型,List<Integer>不是List<Number>的父類型,試圖用如下方式賦值是不容許的:
public static void main(String[] args) { List<Number> numberList = new ArrayList<Number>(); List<Integer> integerList = new ArrayList<Integer>(); numberList = integerList; }
第5行將報錯」Type mismatch: cannot convert from List<Integer> to List<Number>」。有人可能以爲這樣很不方便:我在一個方法裏面只須要循環檢索一個List,也不能利用多態放一個父類型進去,也不能重載,那怎麼辦呢?針對這個問題,Java給開發者提供了通配符」?」,看一下:
public static void main(String[] args) { List<String> stringList = new ArrayList<String>(); List<Integer> integerList = new ArrayList<Integer>(); printList(stringList); printList(integerList); } private static void printList(List<?> l) { for (Object o : l) System.out.println(o); }
<?>是類型通配符,表示是任何泛型的父類型,這樣List<Object>、List<String>這些均可以傳遞進入printList方法中,注意這裏的參數不能寫成List<E>,這樣就報錯了,E未定義。固然<?>也能夠不加,不過這樣會有警告:若是傳遞一個List<E>給List,至關於傳遞一個只承諾將它看成List(原始類型)的方法,這將會破壞使用泛型的類型安全。
再注意一點,使用類型通配符,只能從中檢索元素,不能添加元素。
泛型方法
public static void main(String[] args) { System.out.println(ifThenElse(false, "111", "222")); } private static <T> T ifThenElse(boolean b, T first, T second) { return b ? first : second; }
返回結果爲:
222
這說明,方法也能夠被泛型化,無論定義在其中的類是否是泛型化的。這意味着不用顯式告訴編譯器,想要T什麼值:編譯器只知道這些T都必須相同。
靜態資源不認識泛型
接上一個話題,若是把<T>去掉,那麼:
報錯,T未定義。可是若是咱們再把static去掉:
這並不會有任何問題。兩相對比下,能夠看出static方法並不認識泛型,因此咱們要加上一個<T>,告訴static方法,後面的T是一個泛型。既然static方法不認識泛型,那咱們看一下static變量是否定識泛型:
這證實了,static變量也不認識泛型,其實不只僅是static方法、static變量,static塊也不認識泛型,能夠本身試一下。總結起來就是一句話:靜態資源不認識泛型。
泛型約束
能夠對泛型參數做約束,原本以爲應該有規律,後來發現沒有,那就把本身研究的結論發一下,假設有一組類繼承關係C繼承自B,B繼承自A:
一、定義class的時候只能使用extends關鍵字且不能用通配符」?」
public class TestMain<T extends B> { public static void main(String[] args) { new TestMain<C>(); } }
正確。TestMain類的泛型只能傳B的子類,也就是C。」new TestMain<A>()」、」public class TestMain<? extends B>」、」public class TestMain<T super B>」都是錯誤的寫法
二、做爲方法的參數,泛型可使用」? extends B」或者」? super B」,前者表示實際類型只能夠是B的子類,後者表示實際類型只能夠是B的父類,如下兩種寫法都是正確的:
public static void main(String[] args) { print(new ArrayList<C>()); } public static void print(List<? extends B> list) { } public static void main(String[] args) { print(new ArrayList<A>()); print(new ArrayList<Object>()); } public static void print(List<? super B> list) { }
三、做爲局部變量的參數,泛型可使用」? extends B」或者」? super B」,不過前者好像沒什麼意義,後者表示只能夠傳以B爲父類的對象,因此如下的寫法是正確的:
public static void main(String[] args) { List<? super B> list = new ArrayList<B>(); list.add(new C()); }
不要寫」list.add(new A())」,JDK將會認爲這是類型不匹配的。
歡迎工做一到五年的Java工程師朋友們加入Java架構開發 : 867748702 羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、 Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper, Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料) 合理利用本身每一分每一秒的時間來學習提高本身, 不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!