1、匹配任意類型的通配符
在開發中對象的引用傳遞(向上向下傳遞)是最常見的,但是,在泛型的操作中,在進行引用傳遞的時候泛型類型必須匹配才可以傳遞,否則不能傳遞。
例如,如下沒有進行泛型類型匹配,一個是String,一個是Object類型。
package Thread1; 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 demo1{ public static void main(String args[]){ Info<String> i = new Info<String>() ; // 使用String為泛型類型 i.setVar("MLDN") ; // 設置內容 fun(i) ; //把String泛型類型的i對象傳遞給Object泛型類型的temp。 } public static void fun(Info<Object> temp){ // 接收Object泛型類型的Info對象 System.out.println("內容:" + temp) ; } };
編譯發生錯誤。
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method fun(Info<Object>) in the type demo1 is not applicable for the arguments (Info<String>) at Thread1.demo1.main(demo1.java:18)
泛型對象進行引用傳遞的時候,類型必須一致,如果非要傳遞,則可以將fun方法中Info參數的泛型取消掉(變成 void fun(Info temp))。、
以上確實改進了功能,但是似乎不是很妥當,畢竟之前指定過泛型。
以上程序在fun()方法中使用"Info<?>"的代碼形式,表示可以使用任意的泛型類型對象,這樣的話fun()方法定義就合理了,但是使用以上方法也有需要注意的地方,
即:如果使用“?“接收泛型對象的時候,則不能設置被泛型指定的內容。
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) ; } };
如果使用”?“意味着可以接收任意的內容,但是此內容無法直接使得用”?“修飾的泛型的對象進行修改。如下就會出問題:
package Thread1; 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 demo1{ public static void main(String args[]){ Info<?> i = new Info<String>() ; // 使用String為泛型類型 i.setVar("MLDN") ; // 設置內容,這里會出錯,因為”?“通配符修飾的對象只能接收,不能修改,也就是不能設置。 } };
運行結果:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method setVar(capture#1-of ?) in the type Info<capture#1-of ?> is not applicable for the arguments (String) at Thread1.demo1.main(demo1.java:17)
在使用”?“只能接收,不能修改。
2、受限泛型
之前設置泛型的時候,實際上是可以任意設置的,只要是類就可以設置。但是在JAVA的泛型中可以指定一個泛型的上限和下限。
設置上限
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 GenericsDemo17{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; // 聲明Integer的泛型對象 Info<Float> i2 = new Info<Float>() ; // 聲明Float的泛型對象 i1.setVar(30) ; // 設置整數,自動裝箱 i2.setVar(30.1f) ; // 設置小數,自動裝箱 fun(i1) ; fun(i2) ; } public static void fun(Info<? extends Number> temp){ // 只能接收Number及其Number的子類 System.out.print(temp + "、") ; } };
運行成功。但是,如果傳人的泛型類型為String的話就不行,因為String不是Number子類。
在類中使用泛型上限。
package Thread1; 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 demo1{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; // 聲明Integer的泛型對象 } };
如果在使用Info的時候設置成String類型,則編譯的時候將會出現錯誤(String不是Number子類):
設置下限
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類型的泛型,String類的父類只有Object類 System.out.print(temp + "、") ; } };
Object類和String類都是String的父類,所有運行成功,但是如果此時用Integer則會出錯,因為integer並不是String父類。
3、解釋:泛型與子類繼承的限制。
一個類的子類可以通過對象多態性,為其父類實例化,但是在泛型操作中,子類的泛型類型是無法使用父類的泛型類型接收的。例如:Info<String>不能使用Info<Object>接收。
例如,以下肯定出錯。
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 ; //這里因為對象泛型類型不同,而出錯。 } };
代碼示例:
import java.util.ArrayList; import java.util.List; class Fruit {} class Apple extends Fruit {} class Jonathan extends Apple {} class Orange extends Fruit {} public class CovariantArrays { public static void main(String[] args) { //上界 List<? extends Fruit> flistTop = new ArrayList<Apple>(); flistTop.add(null); //add Fruit對象會報錯 //flist.add(new Fruit()); Fruit fruit1 = flistTop.get(0); //下界 List<? super Apple> flistBottem = new ArrayList<Apple>(); flistBottem.add(new Apple()); flistBottem.add(new Jonathan()); //get Apple對象會報錯 //Apple apple = flistBottem.get(0); } }
這些特點的原因
上界 <? extend Fruit> ,表示所有繼承Fruit的子類,但是具體是哪個子類,無法確定,所以調用add的時候,要add什么類型,誰也不知道。但是get的時候,不管是什么子類,不管追溯多少輩,肯定有個父類是Fruit,所以,我都可以用最大的父類Fruit接着,也就是把所有的子類向上轉型為Fruit。
下界 <? super Apple>,表示Apple的所有父類,包括Fruit,一直可以追溯到老祖宗Object 。那么當我add的時候,我不能add Apple的父類,因為不能確定List里面存放的到底是哪個父類。但是我可以add Apple及其子類。因為不管我的子類是什么類型,它都可以向上轉型為Apple及其所有的父類甚至轉型為Object 。但是當我get的時候,Apple的父類這么多,我用什么接着呢,除了Object,其他的都接不住。
所以,歸根結底可以用一句話表示,那就是編譯器可以支持向上轉型,但不支持向下轉型。具體來講,我可以把Apple對象賦值給Fruit的引用,但是如果把Fruit對象賦值給Apple的引用就必須得用cast。
總結:關於上界下界
上界的list只能get,不能add(確切地說不能add出除null之外的對象,包括Object);
下界的list只能add,不能get;
add和get涉及到具體的數據類型了;
add方法是先給預添加的對象創建一個引用,再讓這個引用指向具體的父類或子類對象;
get方法是返回具體的類(假設為類型1),必須有一個類型1或類型1的父類引用去指向它;
規定了上界:如果add添加對象,java不知道要為哪一個具體的類添加引用,但java不會自動選擇;如果get對象,卻可以(人為地)使用上界類創建引用了(因為代碼中規定了上界,一看便知)。
規定了下界:如果add添加對象,java不知道要為哪一個具體的類添加引用,這里就可以(人為地)添加下界類的子類對象了(因為代碼中規定了下界,一看便知);如果get對象,只能用預獲取的類的‘同類’或父類創建引用,但程序員不知道get出來的是哪個類。