Java中的泛型詳解,深刻解析各類泛型的使用方法| 8月更文挑戰

泛型的基本概念

  • 泛型: 參數化類型
    • 參數:
      • 定義方法時有形參
      • 調用方法時傳遞實參
    • 參數化類型: 將類型由原來的具體的類型參數化,相似方法中的變量參數
      • 類型定義成參數形式, 能夠稱爲類型形參
      • 在使用或者調用時傳入具體的類型,能夠稱爲類型實參
  • 泛型的本質是爲了參數化類型
    • 在不建立新的類型的狀況下,經過泛型指定的不一樣類型來控制形參具體限制的類型
    • 在泛型使用過程當中,操做的數據類型被指定爲一個參數,這種參數類型能夠用在:
      • 類 - 泛型類
      • 接口 - 泛型接口
      • 方法 - 泛型方法
  • 泛型示例:
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
 
arrayList.forEach(i -> {
	String item = (String) arrayList.get(i);
 Log.d("泛型", "item = " + item);
});
複製代碼
  • 這樣的寫法會致使程序出現異常崩潰結束:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
複製代碼
  • 這裏的ArrayList能夠存聽任意類型,添加了一個String類型,添加了一個Integer類型,再使用時都以String的方式使用,所以程序崩潰
  • 泛型就是解決這樣的問題
  • 再討論另外一種狀況,若是將第一行聲明初始的代碼修改一下,那麼在編譯階段就能發現問題:
List arrayList = new ArrayList<String>();
arrayList.add("aaaa");
arrayList.add(100); // 這一步在編譯階段,編譯器就會報錯
 
 arrayList.forEach(i -> {
 	String item = (String) arrayList.get(i);
 	Log.d("泛型", "item = " + item);
 });
複製代碼
  • 泛型只在編譯階段有效

能夠發現,在編譯事後,程序會採起去泛型化措施.也就是說,Java中的泛型,只在編譯階段有效.在編譯過程當中,正確檢驗泛型結果後,會將泛型的相關信息擦除,而且在對象進入和離開方法的邊界處添加類型檢查和類型轉換方法java

  • 泛型類型在邏輯上能夠當作多個不一樣的類型,實際上都是相同的基本類型

泛型的使用

  • 泛型有三種使用方式:
    • 泛型類
    • 泛型接口
    • 泛型方法

泛型類

  • 泛型類: 泛型類型用於類定義中
    • 經過泛型類能夠完成對一組類的操做對外開發相同的接口
    • 最典型的就是各類容器類:
      • List
      • Set
      • Map
  • 泛型類的最基本寫法:
class 類名稱 <泛型標識: 標識號,標識指定的泛型的類型> {
	private 泛型標識 成員變量類型 成員變量名;
}
複製代碼
  • 示例:
/* * 這裏的T能夠爲任意標識,一般使用T,E,K,V等形式的參數表示泛型 * 在實例化泛型時,必須指定T的具體類型 */
 public class Generic<T> {
 	// key這個成員變量的類型爲T,T的類型由外部指定
 	private T key;
	
	// 泛型構造方法形參key的類型也爲T,T的類型由外部指定
	public Generic(T key) {
		this.key = key;
	}

	// 泛型構造方法getKey的返回值類型爲T,T的類型由外部指定
	public T getKey() {
	}
 }
複製代碼
  • 泛型類中不必定要傳入泛型類型的實參:
    • 若是傳入泛型實參,會根據傳入的泛型實參作相應的限制,此時泛型纔會起到本應起到的限制做用
    • 若是不傳如泛型類型的實參,在泛型類中使用泛型的方法或者成員變量的定義能夠爲任何類型
  • 泛型的類型參數只能是類類型,不能是簡單類型
  • 不能對確切的泛型類型使用instanceof操做,編譯時會出錯

泛型接口

  • 泛型接口與泛型類的定義及使用基本相同
  • 泛型接口經常被用在各類類的生產器中
  • 示例:
// 定義一個泛型接口
public interface Generator<T> {
	public T next();
}
複製代碼

泛型通配符

  • Integernumber的一個子類 ,Generic< Integer > 與**Generic< number ** 其實是相同的一種類型
  • 由此,產生以下問題:
    • 在使用Generic< number > 做爲形參的方法中,可否使用Generic< Integer > 的實例傳入?
    • 在邏輯上相似於Generic< number >和Generic< Integer >是否能夠當作是具備父子關係的泛型類型呢?

由此能夠看到Generic< Integer >不能看做是Generic< Number >的子類.數組

  • 因而可知:
    • 同一種泛型能夠對應多個版本,由於參數類型是不肯定的
    • 不一樣版本的泛型類型實例是不兼容的
  • 爲了解決這樣的問題,又不能爲了定義一個新的方法來處理Generic< Integer >,這與Java中多態的理念違背.所以,須要一個在邏輯上能夠表示同時是Generic< Integer >和Generic< Number >父類的引用類型.這樣的類型就是類型通配符:
  • 使用通配符表示泛型:
public void showKeyValueWildcard(Generic<?> obj) {
	Log.d("泛型測試", "key value is" + obj.getKey());
}
複製代碼
  • 類型通配符通常使用 ? 代替具體的類型實參:
    • 此處的 ?類型實參, 而不是類型形參.
    • 和Number,String,Integer同樣,都是一種實際的類型
    • 能夠把 ? 看做是全部類型的父類,是一種真實的類型
  • 類型通配符的使用場景:
    • 當具體類型不肯定的時候,這個通配符就是 ?
    • 當操做類型時,不須要使用類型的具體功能,只使用Object類中的功能,那麼可使用 ? 通配符來表示未知的類型

泛型方法

  • 泛型類: 在實例化類的時候指明泛型的具體類型
  • 泛型方法: 在調用方法的時候指明泛型的具體類型
靜態方法與泛型
  • 注意在類中的靜態方法使用泛型:
    • 靜態方法沒法訪問類上定義的泛型
    • 若是靜態方法操做的引用數據類型不肯定的時候,必需要將泛型定義在方法上
  • 若是靜態方法要使用泛型的話,必須將靜態方法定義成泛型方法:
public class StaticGenerator<T> {
	...
	...
	/* * 若是在類中定義使用泛型的靜態方法,須要添加額外的泛型聲明 - 將這個方法定義成泛型方法 * 不然會報錯: StaticGenerator cannot be refrenced from static context */
	 public static <T> void show(T t) {
	 }
}
複製代碼
泛型方法總結
  • 泛型方法能使方法獨立於類而產生變化,使用原則:
    • 不管什麼時候,若是能作到,就儘可能使用泛型方法
    • 若是使用泛型方法將整個類泛型話,就應該使用泛型方法
    • 對於一個static方法,沒法訪問泛型類型的參數.若是static方法要使用泛型,就必須使之成爲泛型方法

泛型的上下邊界

  • 在使用泛型的時候,能夠爲傳入的泛型類型實參進行上下邊界的限制:
    • 好比: 類型的實參只准傳入某種類型的父類或者某種類型的子類
  • 爲泛型方法添加上邊界,即傳入的類型實參必須是指定類型的子類型
  • 爲泛型類添加上邊界,即類中泛型必須是指定類型的子類型
  • 從上面能夠看出 : 泛型的上下邊界添加,必須與泛型的聲明在一塊兒

泛型數組

  • 在Java中,不能建立一個確切的泛型類型的數組
/* * 這個數組建立的方式是不容許的 * List<String>[] ls = new ArrayList<String>[10]; */
 
 // 使用通配符建立泛型數組是能夠的
 List<?>[] ls = new ArrayList<?>[10];

 // 下面的這個方法也是能夠的
 List<String> ls = new ArrayList[10];
複製代碼
  • 因爲JVM的擦除機制,在運行時JVM是不知道泛型信息的:
    • 全部能夠給oa[1] 賦值一個ArrayList卻不會出現異常
    • 可是在取出數據的時候要作一次類型轉換,就會出現ClassCastException
    • 若是能夠進行泛型數組的聲明,那麼上面的這種狀況在編譯期將不會出現任何警告和錯誤,只有在運行時纔會報錯
  • 經過對泛型數組的聲明進行限制,對於這樣的狀況,能夠在編譯期提示代碼有類型安全問題
  • 數組的類型不能夠是類型變量,除非是採用通配符的方式: 由於對於通配符的方式,最後取出數據是要作顯式的類型轉換的
相關文章
相關標籤/搜索