Java--泛型的原理以及使用場景
Java從1.5之後支援泛型,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。
入不支援泛型,則表現為支援Object,不是特定的泛型。
泛型是對 Java 語言的型別系統的一種擴充套件,以支援建立可以按型別進行引數化的類。可以把型別引數看作是使用引數化型別時指定的型別的一個佔位符,就像方法的形式引數是執行時傳遞的值的佔位符一樣。
可以在集合框架中看到泛型的動機。例如,List類允許您向一個 List新增任意類的物件,即使最常見的情況List.add()。
因為 list.get() 被定義為返回 Object,所以一般必須將 list.get() 的結果強制型別轉換為期望的型別,如下面的程式碼所示:
List list = new ArrayList();
list .add("obj");
String s = (String) list.get(0);
要讓程式通過編譯,必須將 get() 的結果強制型別轉換為 String,並且希望結果真的是一個 String。但是有可能某人已經在該對映中儲存了不是 String 的東西,這樣的話,上面的程式碼將會丟擲 ClassCastException。
理想情況下,您可能會得出這樣一個觀點,即 list 是一個 List,它將 String 鍵對映到 String 值。
泛型可以消除程式碼中的強制型別轉換,同時獲得一個附加的型別檢查層,該檢查層可以防止有人將錯誤型別的鍵或值儲存在集合中。這就是泛型所做的工作。
泛型的好處
Java 語言中引入泛型是一個較大的功能增強。不僅語言、型別系統和編譯器有了較大的變化,以支援泛型,而且類庫也進行了大翻修,所以許多重要的類,比如集合框架,都已經成為泛型化的了。
這帶來了很多好處:
1,型別安全。 泛型的主要目標是提高 Java 程式的型別安全。通過知道使用泛型定義的變數的型別限制,編譯器可以在一個高得多的程度上驗證型別假設。
沒有泛型,這些假設就只存在於程式設計師的頭腦中(或者如果幸運的話,還存在於程式碼註釋中)。
2,消除強制型別轉換。
3,潛在的效能收益。 泛型為較大的優化帶來可能。在泛型的初始實現中,編譯器將強制型別轉換(沒有泛型的話,程式設計師會指定這些強制型別轉換)插入生成的位元組碼中。
但是更多型別資訊可用於編譯器這一事實,為未來版本的 JVM 的優化帶來可能。由於泛型的實現方式,支援泛型(幾乎)不需要 JVM 或類檔案更改。
所有工作都在編譯器中完成,編譯器生成類似於沒有泛型(和強制型別轉換)時所寫的程式碼,只是更能確保型別安全而已。
Java語言引入泛型的好處是安全簡單。泛型的好處是在編譯的時候檢查型別安全,並且所有的強制轉換都是自動和隱式的,提高程式碼的重用率。
泛型在使用中還有一些規則和限制:
1、泛型的型別引數只能是類型別(包括自定義類),不能是簡單型別。
2、同一種泛型可以對應多個版本(因為引數型別是不確定的),不同版本的泛型類例項是不相容的。
3、泛型的型別引數可以有多個。
4、泛型的引數型別可以使用extends語句,例如<T extends superclass>。習慣上成為“有界型別”。
5、泛型的引數型別還可以是萬用字元型別。例如Class<?> classType = Class.forName(Java.lang.String);
泛 型還有介面、方法等等,內容很多,需要花費一番功夫才能理解掌握並熟練應用。在此給出我曾經瞭解泛型時候寫出的兩個例子(根據看的印象寫的),實現同樣的 功能,一個使用了泛型,一個沒有使用,通過對比,可以很快學會泛型的應用,學會這個基本上學會了泛型70%的內容。
泛型的使用場景
萬用字元
在開發中物件的引用傳遞是最常見的,但是在泛型操作中,進行引用傳遞的時候泛型必須匹配才可以傳遞,否則無法傳遞。
class Info<T>{
private T var ; // 定義泛型變數
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
public String toString(){
// 直接列印
return this.var.toString() ;
} };
public class GenericsDemo14{
public static void main(String args[]){ Info<String> i = new Info<String>() ; // 使用String為泛型型別 i.setVar("MLDN") ; // 設定內容 fun(i) ; } public static void fun(Info<?> temp){ // 可以接收任意的泛型物件 System.out.println("內容:" + temp) ; } };
使用?可以接受任意型別的資料,卻無法進行修改,?w為萬用字元。
受限泛型
class Info<T> { private T var; // 定義泛型變數 public T getVar() { return var; } public void setVar(T var) { this.var = var; } public String toString(){ // 直接列印 return var.toString(); } } public class GenericsDemo17 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Info<Integer> info1 = new Info<Integer>(); // 宣告Integer的泛型物件 Info<Float> info2 = new Info<Float>(); // 宣告Float的泛型物件 Info<String> info3 = new Info<String>(); info1.setVar(30); // 設定整數,自動裝箱 info2.setVar(30.1F); // 設定小數,自動裝箱 info3.setVar("俺是字串,不能被受限的FUN組裝"); fun(info1); fun(info2); // fun(info3); //受限了,不能呼叫這個 } /** * 可以接收任意的泛型物件(// 只能接收Number及其Number的子類) * @param temp */ public static void fun(Info<? extends Number> temp){ // 只能接收String或Object型別的泛型 //public static void fun(Info<? super String> temp){ System.out.println("內容:"+temp); } }
不僅僅在使用過程中,也可以在定義類的時候指定泛型上限:
class Info<T extends Number>{ // 此處泛型只能是數字型別 private T var ; // 定義泛型變數 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ // 直接列印 return this.var.toString() ; } }; public class GenericsDemo19{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; // 宣告Integer的泛型物件 } };
如果設定成Stirng型別就會出現錯誤:
GenericsDemo20.java:15: 型別引數 java.lang.String 不在其限制範圍之內 Info<String> i1 = new Info<String>() ; // 宣告Integer的 泛型物件
String 不是Number的子類,最高不能超過Number的子類。
設定下限
泛型適用於本類以及父類型別上的時候,必須使用泛型下限。
如下只能接受String以及String的父類。最低不能接受Stirng類及其父類以外的類。
class Info<T>{ private T var ; // 定義泛型變數 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ // 直接列印 return this.var.toString() ; } }; public class GenericsDemo21{ public static void main(String args[]){ Info<String> i1 = new Info<String>() ; // 宣告String的泛型物件 Info<Object> i2 = new Info<Object>() ; // 宣告Object的泛型物件 i1.setVar("hello") ; i2.setVar(new Object()) ; fun(i1) ; fun(i2) ; } public static void fun(Info<? super String> temp){ // 只能接收String或Object型別的泛型 System.out.print(temp + "、") ; } };
注意:子類無法使用父類的泛型型別進行接受。
class Info<T>{ private T var ; // 定義泛型變數 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ // 直接列印 return this.var.toString() ; } }; public class GenericsDemo23{ public static void main(String args[]){ Info<String> i1 = new Info<String>() ; // 泛型型別為String Info<Object> i2 = null ; i2 = i1 ; } };
就會爆如下錯誤:
GenericsDemo23.java:17: 不相容的型別 找到: Info<java.lang.String> 需要: Info<java.lang.Object> i2 = i1 ; ^
泛型介面:
在jdk1.5以後,不僅僅可以宣告泛型類,也可以宣告泛型介面,泛型介面很類似泛型類:
訪問許可權 +interface +介面名稱 + <泛型標示>{}
泛型介面實現的兩種方式
1:
interface Info<T>{ // 在介面上定義泛型
public T getVar() ; // 定義抽象方法,抽象方法的返回值就是泛型型別 } class InfoImpl implements Info<String>{ // 定義泛型介面的子類 private String var ; // 定義屬性 public InfoImpl(String var){ // 通過構造方法設定屬性內容 this.setVar(var) ; } public void setVar(String var){ this.var = var ; } public String getVar(){ return this.var ; } }; public class GenericsDemo{ public static void main(String arsg[]){ Info i = null; // 宣告介面物件 i = new InfoImpl("soyoungboy") ; // 通過子類例項化物件 System.out.println("內容:" + i.getVar()) ; } };
2:
interface Info<T>{ // 在介面上定義泛型 public T getVar() ; // 定義抽象方法,抽象方法的返回值就是泛型型別 } class InfoImpl<T> implements Info<T>{ // 定義泛型介面的子類 private T var ; // 定義屬性 public InfoImpl(T var){ // 通過構造方法設定屬性內容 this.setVar(var) ; } public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } }; public class GenericsDemo{ public static void main(String arsg[]){ Info<String> i = null; // 宣告介面物件 i = new InfoImpl<String>("soyoungboy") ; // 通過子類例項化物件 System.out.println("內容:" + i.getVar()) ; } };
泛型方法:
泛型方法定義:
訪問許可權 +<泛型標示>+泛型標示 方法名稱(泛型標示 引數名稱)
class Demo{ public <T> T fun(T t){ // 可以接收任意型別的資料 return t ; // 直接把引數返回 } }; public class GenericsDemo{ public static void main(String args[]){ Demo d = new Demo() ; // 例項化Demo物件 String str = d.fun("soyoungboy") ; // 傳遞字串 int i = d.fun(30) ; // 傳遞數字,自動裝箱 System.out.println(str) ; // 輸出內容 System.out.println(i) ; // 輸出內容 } };
通過泛型方法返回泛型類的例項
class Info<T extends Number>{ // 指定上限,只能是數字型別 private T var ; // 此型別由外部決定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆寫Object類中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo27{ public static void main(String args[]){ Info<Integer> i = fun(30) ; System.out.println(i.getVar()) ; } public static <T extends Number> Info<T> fun(T param){ Info<T> temp = new Info<T>() ; // 根據傳入的資料型別例項化Info temp.setVar(param) ; // 將傳遞的內容設定到Info物件的var屬性之中 return temp ; // 返回例項化物件 } };
如果同一方法引數使用泛型,應該保證泛型型別一致:
class Info<T>{ // 指定上限,只能是數字型別 private T var ; // 此型別由外部決定 public T getVar(){ return this.var ; } public void setVar(T var){ this.var = var ; } public String toString(){ // 覆寫Object類中的toString()方法 return this.var.toString() ; } }; public class GenericsDemo{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; Info<String> i2 = new Info<String>() ; i1.setVar(30) ; // 設定內容 i2.setVar("aoyoungboy") ; // 設定內容 add(i1,i2) ; } public static <T> void add(Info<T> i1,Info<T> i2){ System.out.println(i1.getVar() + " " + i2.getVar()) ; } };
就會產生錯誤:
泛型】_泛型的其他應用\程式碼>javac GenericsDemo.java GenericsDemo29.java:19: 無法將 GenericsDemo 中的 <T>add(Info<T>,Info<T>) 應用 於 (Info<java.lang.Integer>,Info<java.lang.String>) add(i1,i2) ; ^
泛型陣列
使用泛型方法的時候,也可以傳遞或者返回一個泛型陣列:
public class GenericsDemo{ public static void main(String args[]){ Integer i[] = fun1(1,2,3,4,5,6) ; // 返回泛型陣列 fun2(i) ; } public static <T> T[] fun1(T...arg){ // 接收可變引數 return arg ; // 返回泛型陣列 } public static <T> void fun2(T param[]){ // 輸出 System.out.print("接收泛型陣列:") ; for(T t:param){ System.out.print(t + "、") ; } } };
泛型巢狀:
class Info<T,V>{ // 接收兩個泛型型別 private T var ; private V value ; public Info(T var,V value){ this.setVar(var) ; this.setValue(value) ; } public void setVar(T var){ this.var = var ; } public void setValue(V value){ this.value = value ; } public T getVar(){ return this.var ; } public V getValue(){ return this.value ; } }; class Demo<S>{ private S info ; public Demo(S info){ this.setInfo(info) ; } public void setInfo(S info){ this.info = info ; } public S getInfo(){ return this.info ; } }; public class GenericsDemo{ public static void main(String args[]){ Demo<Info<String,Integer>> d = null ; // 將Info作為Demo的泛型型別 Info<String,Integer> i = null ; // Info指定兩個泛型型別 i = new Info<String,Integer>("李興華",30) ; // 例項化Info物件 d = new Demo<Info<String,Integer>>(i) ; // 在Demo類中設定Info類的物件 System.out.println("內容一:" + d.getInfo().getVar()) ; System.out.println("內容二:" + d.getInfo().getValue()) ; } };
android中類似於定義Adapter的時候傳入model的泛型