之前面試和這兩天用到泛型解決了一些問題,才發現自己對泛型的瞭解只停留用的時候會想起來一點點....沒有真正的準確梳理泛型,所以想寫一寫總結一下自己對泛型對理解
1、代碼用泛型的好處
在寫代碼的時候經常不同數據類型,而泛型可以統一數據類型,便於操作。
將運行時的異常提前到了編譯時,提高了效率(在jdk1.5中引入了新特性,泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法類型)。
避免了強制類型轉換。
實現代碼到模版化,把數據類型當作參數傳遞,提高了可重用性。
2、泛型
泛型,其實是"參數化類型"。泛型的本質是參數化類型(在不創建新的類型的情況下,通過泛型指定的不同類型來控制形參具體限制的類型)。換句話說在泛型使用過程中,操作數據類型被指定爲一個參數,這種參數類型可以用在類、接口和方法中,分別成爲泛型類,泛型參數,泛型方法。
在jdk 1.5之前,沒有泛型的情況下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,這種轉換需要開發者對實際參數類型可以預知的情況下進行。對於強制類型轉換錯誤的情況,編譯氣可能不提示錯誤,在運行的時候纔出現異常。這時候泛型的好處就體現出來,使用泛型就可以首先通過IDE進行代碼類型初步檢測,然後在編譯階段進行編譯類型檢查,保證了類型轉換的安全性;並且所有的強制轉換都是自動和隱式的,可以提高代碼重用率。
泛型是一種把類型的明確工作推遲到創建對象或者調用方法的時候纔去明確的特殊類型。
注意:類型參數只能代表引用類型,不能是原始類型(int,double,char等)。
3、泛型類、泛型方法、泛型接口
泛型類: class 類名稱 <泛型類型標識>{} 如:public class User<T>{}
/** * Created by mhSui on 2019/09/23. * * @author mhSui */ public class Generics<T>{ private T data; public void setData(T data){ this.data = data; } public T getData(){ return data; } }
注意:一旦在類上聲明泛型,在類裏面所有非靜態成員都可以使用。
泛型方法:[作用域修飾符] <泛型類型標識> [返回類型] 方法名稱(參數列表){} 如:public <T> 返回值 方法名(T a){}
public <E> void show(E s ) { System.out.println(s);}
方法上的泛型和類上的泛型很像,唯一不同的是類型的作用域不同。
在jdk 1.7之後,編譯器可以通過type inference(類型推導),根據實參的類型自動推導出相應的類型參數。
泛型接口:與泛型類的定義很相似。
實現接口的時,聲明類的同時,需要將泛型的聲明也一起放在類中。我們可以爲泛型傳入任意實參,形成不同類型的接口。
4、泛型通配符
想象一下一個場景:Ingeter是Number的一個子類,由於泛型擦除的存在,對於編譯器來說Generic<Ingeter>與Generic<Number>實際上是同一種基本類型。那麼,使用Generics<Number>作爲形參方法中,能否使用Generics<Integer>的實例傳入呢?結果會發現:同一種泛型可以對應多個版本(因爲參數類型是不確定的),而不同版本的泛型類實例之間是不兼容的。
<?>:類型通配符一般是使用?代替具體的類型參數。例如 List<?> 在邏輯上是List<String>,List<Integer> 等所有List<具體類型實參>的父類
<? extends T>:向下限定,只能是T及其子類,泛型上限。
<? super T>:向上限定,只能是T及其父類,泛型下限。
5、類型擦除
類型擦除就是說Java泛型只能用於在編譯期間的靜態類型檢查,然後編譯器生成的代碼會擦除相應的類型信息,這樣到了運行期間實際上JVM根本就知道泛型所代表的具體類型。這樣做的目的是因爲Java泛型是1.5之後才被引入的,爲了保持向下的兼容性,所以只能做類型擦除來兼容以前的非泛型代碼。
6、不允許使用泛型的場景
a) 不能是靜態的類型
public class GenericsExample<T> { private static T member; //This is not allowed }
b) 不能創建T的實例
public class GenericsExample<T> { public GenericsExample(){ new T(); } }
c)在聲明時不能和原生類型一起使用
final List<int> ids = new ArrayList<>(); //不允許 final List<Integer> ids = new ArrayList<>(); //允許
d) 不能創建泛型的異常類
// 引起編譯錯誤 public class GenericException<T> extends Exception {}