Java-泛型
今天記錄一下Java中的泛型,畢竟大家在專案中經常用到或者看到過。
參考: ofollow,noindex">https://www.jianshu.com/p/95f349258afb
1.什麼是泛型
泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數,Java語言引入泛型的好處是安全簡單
2.泛型的特性
在Java SE 1.5之前,沒有泛型的下, 通過對型別Object的引用來實現引數的“任意化” ,“任意化”帶來的缺點是要做顯式的強制型別轉換而這種轉換是要求開發者對實際引數型別預知的情況下進行的。對於強制型別轉換錯誤的情況, 編譯器在編譯期間可能不提示錯誤,在執行的時候才出現異常,這是一個安全隱患。 泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,可以提高程式碼的重用率。
拿我們常用的List集合舉個例子:
List mList =new LinkedList(); mList.add("aaa"); int a = (int) mList.get(0);
不用說這樣一執行肯定會報一個型別不能轉換的錯誤。注意雖然不正確但編譯器沒有發現,執行時才會報錯,那我們試試加泛型的。

image.png
其實我們常用的集合就是加了泛型的,只不過大家可能不在意。
總結:
Object的引數"任意化"在編譯期間不能發現錯誤,執行時可能報錯,是個安全隱患,而泛型可以在編譯期間就可以檢查型別安全。3.常見的泛型分類
泛型有三種分類:泛型類、泛型介面、泛型方法
3.1泛型類
泛型類的定義方式:
publicclass generic<泛型萬用字元>{ private 泛型萬用字元genericClass; //泛型構造方法形參 parameter的型別也為T,T的型別由外部指定 public Generic(T parameter) { this.genericClass =parameter; } }
這個泛型萬用字元可以隨意取值:比如A,B,C,D.(注意得是大寫)但我們通常都取?、K、V各自有不同的含義:
- ?:表示不確定的java型別
- T:(type) 表示具體的一個java型別
- K V (key value) 分別代表java鍵值中的Key Value
- E: (element) 代表Element
我們就以K,V這種的萬用字元定義一個泛型類,程式碼如下:
public interface GenericInter<K,V> { public K getKey(); public V getValue(); } public class GenericClass<K,V> extends GenericInter{ public K key; public V value; public GenericClass(K key, V value) { this.key = key; this.value = value; } @Override public Object getKey() { return key; } @Override public Object getValue() { return value; } }
呼叫:
GenericClass genericClass =new GenericClass("0","張三"); GenericClass mGenericClass =new GenericClass(1,111);
如果要定義超過兩個,三個或三個以上的泛型引數可以使用T1, T2, ..., Tn,列如:
public class GenericClass<T1,T2,T3>{ public T1 t1; public T2 t2; public T3 t3; public GenericClass(T1 t1, T2 t2, T3 t3) { this.t1 = t1; this.t2 = t2; this.t3 = t3; } }
3.2泛型介面
我們上面定義泛型類的時候用的就是泛型介面,泛型介面與泛型類的定義及使用基本相同。泛型介面常被用在各種類的生產器中:
public interface GenericInter<K,V> { public K getKey(); public V getValue(); }
3.3泛型方法
先說一下泛型類和泛型方法的區別: 泛型類,是在例項化類的時候指明泛型的具體型別;泛型方法,是在呼叫方法的時候指明泛型的具體型別 。 泛型方法相對比較複雜,我們來看一下:
3.3.1

image.png
大家覺得getT()是泛型方法不?答案是:No,
getT()方法雖然使用了泛型,但它的泛型是在泛型類中已經聲明瞭的泛型,所以它才可以使用這個T泛型,getT()不是泛型方法,它算是一個普通成員方法。

image.png
大家覺得showGeneric(genericClass<?> obj)是泛型方法不?答案是:No,
showGeneric(genericClass<?> obj)也是一個普通的方法,只不過使用了泛型萬用字元?,這也印證了泛型萬用字元?是一種型別實參

image.png
真正的泛型方法長這樣:
class genericClass<T> { public T t; public genericClass(T t) { this.t = t; } public T getT(){ return t; } //正確的泛型方法 public <T> T getGenericClassT(genericClass<T> obj){ return obj.getT(); } }
要點:真正的泛型方法,在public與返回值之間的
必不可少,這表明這是一個泛型方法,並且聲明瞭一個泛型T。但T可以出現在這個泛型方法的任意位置, 泛型的數量也可以為任意多個
3.3.2 泛型方法和可變引數

image.png
這個沒啥好說的。
3.3.3 泛型陣列
關於如何正確建立泛型陣列:
//錯誤建立泛型陣列的方式 List<String> [] mList=new ArrayList<String>[10]; //正確建立泛型陣列方式一,通過萬用字元? List<?> [] mListTwo=new ArrayList<?>[10]; //正確建立泛型陣列方式二 List<String>[] mListThree = new ArrayList[10];
總結:在java中是不能建立一個確切的泛型型別的陣列
4.各種泛型的區別
4.1 List<T>,List<Object>,List<?>的區別?
ArrayList<T> al=new ArrayList<T>(); 指定集合元素只能是T型別
ArrayList<?> al=new ArrayList<?>(); 集合元素可以是任意型別,沒有意義,一般只是為了說明用法.
- Object和T不同點在於,Object是一個實打實的類,並沒有泛指誰,而T可以泛指Object,比方public void printList(List<T> list){}方法中可以傳入List<Object> list型別引數,也可以傳入List<String> list型別引數,但是public void printList(List<Object> list){}就只可以傳入List<Object> list型別引數,因為Object型別並沒有泛指誰,是一個確定的型別
- ?和T區別是?是一個不確定類,?和T都表示不確定的型別 ,但如果是T的話,函式裡面可以對T進行操作,比方 T car = getCar(),而不能用? car = getCar()。
4.2 T,Class,Class<T>,Class<?>區別?
- T,是一種具體的類,例如String,List,Map......等等,這些都是屬於具體的類。
- Class,是一個類,但Class是存放上面String,List,Map.....。
注:獲取Class的三種方式:
1. 呼叫Object類的getClass()方法來得到Class物件
image.png
-
使用Class類的中靜態forName()方法獲得與字串對應的Class物件
image.png
3. 如果T是Java型別則直接T.calss

image.png
- Classy<T> 在例項化的時候,T要替換成具體類,使用Class<T>還有一個好處就是不用強轉.
Classy<T> 在例項化的時候,T要替換成具體類,使用Class<T>還有一個好處就是不用強轉.
我們看一下不加泛型,反射建立一個類物件,需要強轉:我們看一下不加泛型,反射建立一個類物件,需要強轉:
image.png
使用Class<T>泛型後,不用強轉了:
image.png
- Class<?>它是個通配泛型,?可以代表任何型別,主要用於宣告時的限制情況
Class<?>它是個通配泛型,?可以代表任何型別,主要用於宣告時的限制情況
例如,你可以宣告:
public Class<?> clazz;
但不能宣告:
public Class<T> clazz;
因為T需要指定型別,所以當不知道定宣告什麼型別的Class的時候可以定義一個Class<?>,Class<?>可以用於引數型別定義,方法返回值定義等。
以上就是今天的全部內容!