Java:高级之泛型概念引入,泛型可以设置多个类型参数,泛型继承和泛型接口实现,限制泛型可用类型,泛型通配的方式,泛型方法,泛型方法限制泛型可用类型
目錄頁
? ? ? ? 泛型簡介
? ? ? ? 泛型類
? ? ? ? 限制泛型可用類型
? ? ? ? 類型通配聲明
? ? ? ? 泛型方法
問題引入
? ? ? ? 如果我們需要產生多個對象,每個對象的邏輯完全一樣,只是對象內的成員變量的類型不同。那我們如何去做?
我們新建一個工程
做一個構造方法????????public Cls1(int a){
?? ??? ?????????????????????????????????this.a =a;
?? ?????????????????????????????????????????}
然后我們實例化這個方法的時候,給他傳一個10????????Cls1 cls1 = new Cls1(10);
然后我們輸出cls1 里面的getData()????????????????System.out.println(cls1.getData());
?運行結果
?同樣的我們在做一個類,唯一的區別,所有的東西都一樣,但是它a變成了????????String a;
?運行結果
你會發現有兩個類,這兩個類的邏輯完全一樣,所謂的邏輯就是里面的一些方法,包括構造方法,只是成員變量的類型不同
?那比如我們現在要做了double 類型的,是不是還得專門為double 創建一個類呀?
? ? ? ? ??
問題解決
? ? ? ? 創建多個類文件,給每個類中的成員變量設置指定的數據類型
? ? ? ? ? ? ? ? 缺點:這種方法會導致類的膨脹,重用性太差。
? ? ? ? 創建一個類文件,給這個類中的成員變量設置Object數據類型。
? ? ? ? ? ? ? ? 缺點:編譯的時候正常,但運行的時候可能會異常
?創建多個類文件,給每個類中的成員變量設置指定的數據類型:
這樣整個業務場景下來,整形一個,小數一個,字符一個,甚至里面會集成一些其他的類。
創建一個類文件,給這個類中的成員變量設置Object數據類型。:
我們會發現整形int ,打開它的類型繼承
?你會發現Object是所有類型的父類
這種情況下我們完全可以把這兩個刪掉一個,
然后我們把Cls2改成Cls1? ? ? ? 第21行
發現沒寫錯,可是還報錯? ? ? ? 錯誤提示:? ? ?The constructor Cls1(int) is undefined
?發現是版本的問題
我們右擊,然后選擇properties
?然后看到JDK那行了嘛?
?
把對勾去掉
然后選擇1.5版本往后的點擊ok
?這個時候就沒有報錯了
?運行一下
?但是他有一個不太好的地方
這個Object導致整個類對這個變量的屬性很模糊
可能會產生某些錯誤,
假如我現在輸出“冰糖李子”,編碼過程中誤操作了,把“冰糖李子”這個字符串強制轉換成整型數,你會發現編譯的時候是ok 的
但是運行的時候會出現異常,類型轉化異常
難免我們在操作過程中,隨著代碼量的增大,對里面的object的類型模糊不清晰,導致你程序員在編寫代碼的時候,做了一個誤判,做了一個錯誤的轉換,導致程序崩潰
面向于 這種情況發生,我們有一個非常好的解決辦法,就是泛型
一、泛型簡介? ? ? ? JDK1.5之后引入?
? ? ? ? 泛型可以在編譯的時候檢查類型安全,并且所有的強制轉換都是自動和隱式的
? ? ? ? 泛型的原理就是“類型的參數化”,即把類型看做參數。也就是說把所要操作的數據類型看做參數,就像方法的形式參數是運行時傳遞的值的占位符一樣。
? ? ? ? 簡單的說,類型變量扮演的角色就如同一個參數,它提供給編譯器用來類型檢查的信息。
? ? ? ? 泛型可以提高代碼的擴展性和重用性
那么泛型怎么做?
示例----泛型類
? ? ? ? public? class GenClass<T>{
? ? ? ? ? ? ? ? private ??T? obj;
????????
? ? ? ? ? ? ? ? public? GenClass(T? ?obj){
? ? ? ? ? ? ? ? ? ? ? ? this.obj = obj;
????????????????}? ? ? ? ?
? ? ? ? ? ? ? ? public? T? getobj(){
? ? ? ? ? ? ? ? ? ? ? ? return obj;
????????????????}
? ? ? ? ? ? ? ? public? void? ?setobj(T? obj){
? ? ? ? ? ? ? ? ? ? ? ? this .obj? = obj;
????????????????}
}
泛型的意思就是就是在class后面添加一個<T>,用T去替代里面未名的數據類型
比如說我們的代碼可以在Cls1后面加上<T>,用一個T來表示
然后new 的時候要把這個東西用上,你在實例化的時候呈現,他是一個整形
Cls1<Integer> cls1= new Cls1<Integer>(10);
同樣使用cls2的時候,落地的時候,我讓他都是字符串
Cls1<String> cls2 = new Cls1<String>("冰糖李子");
?運行結果
我們用<T>替代了剛才寫的Object,在使用的時候,在程序員編碼的過程當中,實例化類的時候,在<>里面,去告訴操作系統,包括程序員看代碼的時候更具象的把他表達出來
這就叫做泛型
泛型可以在編譯的時候檢查類型安全,并且所有的強制轉換都是自動和隱式的
當你轉換出現問題的時候,你硬要把string轉化成Integer,他就會報錯,
剛才我們用object的時候它沒有任何的報錯,
只是在程序運行的時候出現類型轉換異常
(這就是泛型引入的除了程序員可讀之外,還有個好處就是防止你的誤操作,誤轉換)
Cannot cast from String to Integer
?? ? ? ? 泛型的原理就是“類型的參數化”,即把類型看做參數。也就是說把所要操作的數據類型看做參數,就像方法的形式參數是運行時傳遞的值的占位符一樣。:
我們習慣了函數調用,習慣了傳參public Cls1(T a),
那實際上類型也可以以參數的形式傳過來,為了區別我們用來了<>
我們在這個類初始化,實例化的時候?? ?Cls1<Integer> cls1= new Cls1<Integer>(10);
我們直接把泛型類里面的,涉及到泛型的位置,用Integer替代進去
也就是說Cls1類里面的? a變量的的類型,????????T a;
是根據我們真正實例化的時候,用<>的方式傳參過去的,來確定里面a的類型
,同樣的一個類,你在使用實例化的時候,給泛型傳的類型不一樣,這個類最后體現的結果也是不一樣的
????????????????????????????????Cls1<Integer> cls1= new Cls1<Integer>(10);
?? ??? ??????????????????????????? ?System.out.println(cls1.getData());
?? ??? ??????????????????????????? ?Cls1<String> cls2 = new Cls1<String>("冰糖李子");
?? ??? ??? ?????????????????????????????????System.out.println(cls2.getData());
?二、泛型類及特點
? ? ? ? 1、泛型的類型參數可以是泛型類
? ? ? ? 2、泛型類可以同時設置多個類型參數
? ? ? ? 3、泛型類可以繼承泛型類
? ? ? ? 4、泛型類可以實現泛型接口
?泛型的類型參數可以是泛型類:
也就是說你這個<T>除了普通變量Integer? ?和String 外,他也能接受是個泛型
泛型類可以繼承泛型類:
跟我們普通類的繼承是一樣的,當然你泛型可以在繼承過程中實現多個類型參數
你的父類型是個泛型,他里面只有一個<T>,你完全可以除了<T>,多一個<K>?呀,<T2>等等。
一般我們泛型用的<T>,不是寫死的 ,就像形參的名字,你愿意起什么都可以的,
按照程序員編碼習慣,一般是<T>呀,<K>,<B>呀,經常用的幾種寫法
泛型類可以實現泛型接口:
這個繼承和實現,和我們普通類的繼承和實現沒有很大的區別,唯一的區別就是體現在參數上面,和泛型上面
泛型類可以同時設置多個類型參數:
現在再做一個類Cls2,多個參數用,隔開????????class? Cls2<T,T2>
構造方法????????????????????????public Cls2(T a,T2 b){
?? ??????????????????????????????????????????? ?this.a =a;
? ? ? ? ????????????????????????????????????????this.b =b;
?????????????????????????????????????????? ?}
有兩個變量,????????????????T a;
?? ?????????????????????????????????T2 b;
這兩個變量的具體類型為止
同樣的b的類型是T2,????????public T getData(){
?? ??? ?????????????????????????????????????????return a;
?? ?????????????????????????????????????????}
?? ?
?? ?????????????????????????????????????????public T2 getData2(){
?? ??????????????????????????????????????????????????? ?return b;
?????????????????????????????????????????????????? ?}
我們在真正做他的時候,<>里面的順序一樣,構造方法要傳參
? ?
?運行結果
? ? ? ? ? ?
?這面要注意一個問題,比如說我現在有一個Cls4,兩個泛型我們都是整型數
這面顯示一個錯誤?
he constructor Cls2<Integer,Integer>(int, String) is undefined
你這面都寫integer,但是他會認定你傳進來的構造方法是一個字符串,所以他會失敗
?我改成10就OK啦
?
?那么我們可以把上面兩個整型數拿來相加嘛?
運行結果?
?暫時看到編譯是ok 的,加也是可以正常加的
?那如果一個是Integer 一個是String呢
數據類型不同的時候相加,看結果
?
運行結果????????
也能加????????
用System.out.println可加的????????????????
但是會給人家錯誤的認為,感覺不同的數據類型都可以拿來加,????????????????????????????????????????
但是你要知道System.out.println這面的加號是起到一個連接的作用????????
?如果我們單獨給他拎出來,他會出現一個什么樣的情況呢?
????????int sum = 0;
?? ??? ??? ?sum = cls4.getData()+cls4.getData2();
?? ??? ??? ?System.out.println(sum);
?? ??? ??? ?
?
?運行結果
這樣子寫也行?
泛型的類型參數可以是泛型類:
Cls1<Integer> cls1= new Cls1<Integer>(10);里面也可以放入泛型
比如說里面就是Cls1,Cls1他就是個泛型,這邊我用Integer給他確認下來
????????????????????????Cls1<Cls1<Integer>> cls1
另一面也一樣
這面出現問題是因為不能寫10,
?因為他應該是Cls1<Integer>的實例化?? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
泛型類里面的類型還是個泛型,你new的時候和前面一樣,這面的構造方法呢?
?構造方法傳過來的時候 ,T不就變成Cls1<Integer>
我們在輸出的時候,System.out.println(cls1.getData());
這個Data,get出來是一個泛型
因為Cls1他是最外面的那一層,你獲得出來的應該是Cls1<Integer>,
Cls1<Cls1<Integer>> cls1= new Cls1<Cls1<Integer>>(new Cls1<Integer>(10));
? ? ?
這個Cls1<Integer>又是一個類,你如果訪問到a,你還得getData一次
????????System.out.println(cls1.getData().getData());
這個時候我們才能把10抓出來
?運行結果
泛型類可以繼承泛型類:
我現在的Cls2和Cls1是重復的呀,這樣寫也是一種浪費?
可以 ????????class Cls2<T,T2> extends Cls1<T>,繼承過來兒子比父親多點特性,是ok的,
就是構造方法這面我們需要做一些修改
錯誤提示:????????Implicit super constructor Cls1<T>() is undefined. Must explicitly invoke another constructor
因為他要調用父親的?????????? ?super(a);? ? ? ? 你在子的構造方法中,調用父親的構造方法來構造自己
剩下的一項,? ?this.b=b; ? ? 作為數據的初始化
?可以把父類有的去掉
?然后我們使用一下
Cls2<Integer,String> cls = new Cls2<Integer,String>(100,"冰糖李子");
用法還是一樣的,就是我們在構造Cls2的時候,不要寫那么多代碼,一些東西是從Cls1繼承過來的?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
那如果他是繼承的話,有一些函數,比如說???????????void printInfo();????????假設他現在是個泛型,
同時又是一個抽象的方法? ?? ? ? ? ?????????????????abstract?void printInfo();?
這邊是一個抽象帶有泛型的類????????abstract class Cls1<T>? ? ? ? 也是ok的
也是遵循以前繼承的道理,你要把里面 的printInfo()實現出來
錯誤提示:???????? Multiple markers at this line
?? ?????????????????????????- Cannot instantiate the type?
?? ? ????????????????????????Cls1<Cls1<Integer>>
因為abstract class Cls1<T>? ? ? ? 是一個抽象
抽象類是不能初始化的,可以去掉? ? 第39行,第40行
?????代碼如下
?
?運行結果
?泛型類可以實現泛型接口:
我們比如說再來一個interface cls3<T>? ? ? ? 也是泛型
里面有一個抽象方法
這是一個接口
跟以前的寫法也是一樣的,
以前的寫法? 是? ? ? 沒有<T>,然后
????????????????????????????????????????interface cls3
????????????????????????????????????????????????{
?? ?????????????????????????????????????????????????abstract void printInfoCls3(String t);
????????????????????????????????????????????????}
這就是我們以前說的接口,無非在這個接口上加了一個泛型
也就是這個抽象方法,可以實現,輸出整型數,字符串都是ok的
?那我們的cls2可以繼承cls1
?Cls2也可以implements這個接口
?class Cls2<T,T2> extends Cls1<T> implements Cls3<T>
既然你要實現接口的話,就要實現接口里面的方法
?我們在使用Cls2的時候? ? ? ? 第53行
????????????????????????cls.printInfoCls3(100);
雖然Cls2那面寫的都是T,但是??????public Cls2(T a,T2 b){
?? ??? ?????????????????????????????????????????????????super(a);
?? ??????????????????????????????????????????????????????????? ?this.b=b;
?? ?????????????????????????????????????????????????}
這兩個T不一樣????????????????public void printInfoCls3(T t) {
?? ??????????????????????????????????????????? ?// TODO Auto-generated method stub
?? ??? ?????????????????????????????????????????System.out.println(t);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
下面這個T是隸屬于????????interface Cls3<T>這個接口的,取決于你傳遞進來是什么數據
上面的那個T是????????Cls2<Integer,String> cls = new Cls2<Integer,String>(100,"冰糖李子");里面的,它取決于你給他實例化的時候,設定的是具體的哪個參數
當然,剛好我們寫了Integer? ?和?? ?cls.printInfoCls3(100);? 里面的100
如果我們把????????cls.printInfoCls3(100); 改成字符串呢?? ? ? ? 第53行
錯誤提示:The method printInfoCls3(Integer) in the type Cls2<Integer,String> is not applicable for the arguments (String)? ? ? ? 不行了
所以說T如果產生某種沖突的話,也是不允許的
?你這時候即繼承了Cls1都叫T,又實現了這個接口,在這種情況下,你如果名字一樣
你要遵循在class Cls2<T,T2> extends Cls1<T> implements Cls3<T>
????????下面所有的T表示的都是同一種類型
不然他這面會產生類型上的沖突
?
?這種情況下改變一個T3試試
?我把class Cls2<T,T2,T3> extends Cls1<T> implements Cls3<T3>
里面的T3去掉可以嗎?? ? ? ? 第22行
也不行,因為public void printInfoCls3(T3 t)
必須在class Cls2<T,T2,T3> extends Cls1<T> implements Cls3<T3>
被包含
?你要支持T3,你就要做出一系列的修改
Cls2<Integer,String> cls = new Cls2<Integer,String>(100,"冰糖李子");你要跟T3綁定起來
而且必須是一個String
如果這時候你把?cls.printInfoCls3("測試");? ?改成cls.printInfoCls3(100);
也不行
?完整代碼
?
運行結果
?注意:泛型類可以實現泛型接口,
但是要注意,既要繼承又要實現接口的 時候,要保證它里面具備了這些泛型的占位符
三、限制泛型可用類型
????????在定義泛型類別時,默認在實例化泛型類的時候可以使用任何類型,但是如果想要限制使用泛型類型時,只能用某個特定類型或者是其子類型才能實例化該類型時,可以在定義類型時,使用extends關鍵字指定這個類型必須是繼承某個類,或者實現某個接口。
? ? ? ? 當沒有指定泛型繼承的類型或接口時,默認使用extends Object,所以默認情況下任何類型都可以做為參數傳入
? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ?
?當沒有指定泛型繼承的類型或接口時,默認使用extends Object,所以默認情況下任何類型都可以做為參數傳入:
這就是我們之前的做法
?
?系統在編譯它的時候是默認加了一個extends Object
你寫不寫它的效果是一樣的,也就是對T他沒有任何的限制,所以在使用它的時候任何類型都可以作為參數傳入
?
?那如果某種場景我們必須對這個T做一些監管,限制使用泛型類型,
可以使用extends關鍵字指定這個類型必須是繼承某個類,或者實現某個接口。
如果我變成了????????abstract class Cls1<T extends String>
如果你這樣寫,你沒辦法給這面賦一個整型數的
?
?這個時候只能把
?? ?Cls2<Integer,String,String> cls = new Cls2<Integer,String,String>(100,"冰糖李子");
Integer? ? 變成? ?String類型? ? ? ? 這面的100也只能改成字符串
Cls2<String,String,String> cls = new Cls2<String,String,String>("","冰糖李子");
這就是我們說的限定了T,
?
?可以用繼承,可以用某個接口
我們添加一些具體的寫法進來
我要求這個T必須繼承Animal? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ??
一寫進來Cls2<String,String,String> cls = new Cls2<String,String,String>("","冰糖李子");
必須變成? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
Cls2<Dog,String,String> cls = new Cls2<Dog,String,String>(new Dog(),"冰糖李子");
?
?那如果我現在有一個接口,接口里面有一個抽象的方法? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
interface Move? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
{? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?? ?abstract void test();
}
如果你對于T做一些限制要求,必須要實現Move這個接口? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
我們以前實現接口是????????class A implements Move {? ? ? ? ? ? ??
????????????????????????????????????????????????}? ? ? ?這樣一個寫法
A這面去實現Move里面未實現的方法·? ? ? ? 叫做test
?如果這面也用Move的話? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
abstract class Cls1<T extends Move>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
是用? extends? ? ? abstract class Cls1<T extends Move>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
還是????implements?????abstract class Cls1<T implements Animal>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
發現還是得用extends? ? ? abstract class Cls1<T extends Move>? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
注意他跟我們傳統的????????implements是不一樣的? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?
?
?為什么????????abstract class Cls1<T extends Move>? ? ? ? ? ?多了extends?
因為他可能繼承某個類? ? ? ? ,或者限定了它實現某個接口? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
四、類型通配聲明
同一泛型類,如果實例化時給定的實際類型不同,則這些實例的類型是不兼容的,不能互相賦值。
????????Generic<Boolean> f1 = new Generic<Boolean>();
? ? ? ? Generic<Integer> f2 = new Generic<Integer>();
? ? ? ? f1 = f2;? ? ? ? ? //發生編譯錯誤
? ? ? ? Generic<Object> f= f1 ;? ? ? ? ?//f1和f類型并不兼容,發生編譯錯誤f=f2;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//f2和f類型同樣不兼容,也會發生編譯錯誤
? ? ? ? 泛型類實例之間的不兼容性會帶來使用的不便。我們可以使用泛型類通配符(?)聲明泛型類的變量就可以解決這個問題。
新建一個工程
?現在有一個Cls1,實例化一下(記得抽象類不能實例化)
?同樣的我們再來一個c2,來一個Double類型的,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
然后我們讓c1 = c2;? ? ? ? 他是不允許的? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
錯誤提示????????Type mismatch: cannot convert from Cls1<Double> to Cls1<Integer>
不能把c2轉化為c1,因為Double和Integer是有區別的? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?那如果我們現在有Object,行不行?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
更大的類,去做一些匹配? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
錯誤提示? ?Type mismatch: cannot convert from Cls1<Object> to Cls1<Integer>
他也是不行的? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
反過來也不行
類型通配聲明? ? ? ? 例子
? ? ? ? 泛型通配的方式
? ? ? ·? “?”代表任意一個類型? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ????????? Generic<Boolean>? f1 = new Generic<Boolean>();? ? ? ? ? ? ??
? ? ? ????????? Generic<?> f = f1;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ·和限制泛型的上限相似,同樣可以使用extends關鍵字限定通配符的上限? ? ? ? ? ? ??
? ? ? ????????? Generic<Dog> f1 = new Generic<Dog>();? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ????????Generic<? extends Annimal> f =f1;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ?·?還可以使用super關鍵字將通配符類型限定為某個類型的下限
? ? ? ? ????????Generic <Animal> f1 = new Generic<Animal>();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ????????Generic<? super Dog> f =f1;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? //它必須是Dog 的父親
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
也就是說我們用一個<?? 就可以搞定了,我們聲明這個c3;
這個時候你會發現c3可以引用c1
?同樣的c3可以引用c2;
這個就是我們說的通配符,用?來表示
和限制泛型的上限相似,同樣可以使用extends關鍵字限定通配符的上限:
也就是說,你可以管理這個問號
比如說我們來個c4,我們c4這面要求是一個Dog
比如說我的c4里面的泛型是一個Dog
這種情況下我的c3可以等于c4,因為這個問號是通配所有的
?那如果對于問號管制的話,????????Cls1<? extends String> c3;
發現很多東西都不能用了,因為他必須是String 的子類或者String
?那如果我們把他改成Integer 呢?
發現只有c1這一條是可以的
?如果改成Animal?????????????????Cls1<? extends Animal> c3;呢?
1和2 不行,4是可以的,????????????????對?號產生了一定的范圍
只有它的上限是Animal 的時候才可以
?還可以使用super關鍵字將通配符類型限定為某個類型的下限:
比如我這面用super,就不行了,它必須是Animal的父親
?如果把這面改成一個Dog 呢?
?改成Animal也是可以的
? ? ?
?五、泛型方法
? ? ? ? 不僅類可以聲明泛型,類中的方法也可以聲明僅用于自身的泛型,這種方法叫做泛型方法。其定義格式為:
? ? ? ? 訪問修飾符? <泛型列表>? 返回類型? 方法名? (參數列表){
? ? ? ? ? ? ? ? 實現代碼
????????}
? ? ? ? 在泛型列表中聲明的泛型,可用于該方法的返回類型聲明、參數類型聲明和方法代碼中的局部變量的類型聲明
? ? ? ? 類中其他方法不能使用當前方法聲明的泛型
? ? ? ? 提示:是否擁有泛型方法,與其所在的類是否泛型沒有關系。要定義泛型方法,只需將泛型參數列表置于返回值前
?不僅類可以聲明泛型,類中的方法也可以聲明僅用于自身的泛型,這種方法叫做泛型方法。:
也就是說,這個泛型是屬于方法的,不是屬于類的,也就是說我們可以在普通類中去定義一個泛型方法。
類中其他方法不能使用當前方法聲明的泛型:
也就是說我這個泛型是有時效性的。它是有區域性的,有一個它的作用域,有點像局部變量。你在當前方法中有泛型列表,在其他方法中不能用
是否擁有泛型方法,與其所在的類是否泛型沒有關系。要定義泛型方法,只需將泛型參數列表置于返回值前:
也就是我們說的普通類可以有泛型方法
? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ??
你有了泛型類,為何還要泛型方法呢?
新建一個工程
假設classA里面有一個泛型<T>
? ? ? ? ??
錯誤提示:Multiple markers at this line
?? ?- Syntax error, type parameters are only available if source level is 1.5 or?
?? ? greater
需要換到1.5版本以后的
找到這里?
?
?看見JDK那行了嘛
對勾去掉,換成1.5版本以后的
?然后就沒有錯誤提示了
?運行結果,可行
?那如果我在這里打印一個100呢?
編譯都通不過
錯誤提示:????????The method printInfo(String) in the type A<String> is not applicable for the arguments (int)
因為他要求你傳String,但是你的arguments是100
?等于說這個方法,
被里面的泛型給限制了????????????????class A<T>
???????????????????????????????????????????? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??? public void printInfo(T t){
?? ?????????????????????????????????????????????????????? ????????System.out.println(t);
?? ????????????????????????????????????????????????????????????}
????????????????????????????????????????????????????????}
那么泛型方法是什么意思呢?我不應該讓我的t ????????被你的類限制掉
比如說我現在有一個class? B
我可以把他聲明為普通的方法????????class B<T>
注意:泛型方法是????????
? 訪問修飾符? <泛型列表>? 返回類型? 方法名? (參數列表){
? ? ? ? ? ? ? ? 實現代碼
????????}
也就是說我的泛型列表可以放在返回類型的前面? ? ? ? void前面我放一個<T>
????????????????public <T> void printInfo(T t){
?? ??? ?????????????????????????System.out.println(t);
?????????????????????????? ?}
?
?運行結果
我們得出的結論,泛型方法更加靈活,它可以不受泛型類的約束
如果你泛型類在實例化的時候,確定了泛型的類型,以及你方法里面所有的東西? ?T??都被定死掉了
就出現了剛才我們的A ????????只能打哈哈,不能打1234
這個東西比我們以前做的方法的重載,更加的牛逼
?以前方法重載是什么意思呢?
????????????????public void print1(int a){
?? ??????????????????? ?System.out.println(a);
?????????????????? ?}
再來個char a 這就叫方法的重載
????????????????public void print1(char a){
?? ??????????????????????????????????? ?System.out.println(a);
?????????????????? ?}
? ? ? ??
你即便方法重載也要寫這么多遍
那泛型方法它的好處,比方法重載更加牛逼
不僅類可以聲明泛型,類中的方法也可以聲明僅用于自身的泛型,這種方法叫做泛型方法:
也就是我的T? ? ? ? ?第11行,只對我本方法有效
跟這個類class? B沒有關系,跟其他的方法也沒有關系
也就是說你能不能用到t????????呀?
錯誤提示:? t cannot be resolved to a variable
?當然泛型列表中的個數也可以做多個????????????????
????????public <T,T2> void printInfo(T t,T2 t2){
?? ??? ?????????????????System.out.println(t+t2);
?? ?????????}
?? ?
這其實也是個方法的重載,但是這面t2不讓加? ?
錯誤提示:he operator + is undefined for the argument type(s) T, T2
?分開寫呢?
?
?為什么不讓加呢?因為他如果在后面實例化,確定類型以后是可讓加的,
,你這面沒有實例化,它的方法類型是不確定的,不讓你亂加
?
?當然????????public <T,T2> void printInfo(T t,T2 t2){
?? ??? ?????????????????????????System.out.println(t);
?? ??? ?????????????????????????System.out.println(t2);
?? ?????????????????}
也是泛型方法,只是對????????????????public <T> void printInfo(T t){
?? ??? ?????????????????????????????????????????????????System.out.println(t);
?????????????????????????????????????????????????????????? ?}
做了重載
我們重載的意思是,返回值,跟這個名字一樣,參數列表可以不一樣
?在泛型列表中聲明的泛型,可用于該方法的返回類型聲明、參數類型聲明和方法代碼中的局部變量的類型聲明:
也就是說?????????? ?public <T,T2> void printInfo(T t,T2 t2){
?? ??????????????????????????? ?System.out.println(t);
?? ??????????????????????????????????? ?System.out.println(t2);
?????????????????????????????????? ?}
可以返回一個T? ??public <T,T2> T printInfo(T t,T2 t2){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?System.out.println(t);
?? ???????????????????????????????????????? System.out.println(t2);
?? ??? ?
?? ??? ?????????????????????????????????????????return t;
? ? ? ? ?????????????????????????????????}
這樣子是可以的
?
類型聲明:????????public <T,T2> T printInfo(T t,T2 t2)
局部變量:?? ??????????? ?System.out.println(t);
五、泛型方法
? ? ? ? 什么時候使用泛型方法,而不是泛型類呢???
? ? ? ? ? ? ? ? 添加類型約束只作用于一個方法的多個參數之間,而不涉及類中的其他方法時。
? ? ? ? ? ? ? ? 施加類型約束的方法為靜態方法,只能將其定義為泛型方法,因為靜態方法不能使用其所在類的類型參數
? ? ? ? ? ? ? ? ? ? ?
? 添加類型約束只作用于一個方法的多個參數之間,而不涉及類中的其他方法時。:
我們不希望這個方法當中的數據類型被這個類給限制了,而是自由的我想打印什么,都可以,不被類的方法限制的時候
施加類型約束的方法為靜態方法,只能將其定義為泛型方法,因為靜態方法不能使用其所在類的類型參數:
類型約束:
也就是說????????public <T> void printInfo(T t)這個泛型T也可以進行類型的約束
? ??
? ?前面加上
????????????????????????class Animal
????????????????????????????????{?? ?
?????????????????????????????????? ?public void eat(){
?? ??? ?????????????????????????????????System.out.println("動物吃");
?? ????????????????????????????????????}
????????????????????????????????}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?class Dog extends Animal
????????????????????????????????{
?? ?????????????????????????????????public void eat(){
?? ??????????????????????????????????? ?System.out.println("啃骨頭");
?? ?????????????????????????????????????????}
????????????????????????????????????????}
????????????????????????????????class Cat extends Animal
????????????????????????????????{
?? ?????????????????????????????????public void eat(){
?? ??? ?????????????????????????????????System.out.println("吃魚");
?? ?????????????????????????????????????????}
????????????????????????????????????????}
中間加上????
他必須是一個上限,是Animal的子類? ? ? ??
????????????????????????????public <T extends Animal> void printInfo2(T t){
?? ??????????????????????????????????????????? ?t.eat();
?? ?????????????????????????????????????????}
這就叫類型約束
這時候來個?????????? ?b.printInfo2("哈哈");? 可以嘛?
錯誤提示:Bound mismatch: The generic method printInfo2(T) of type B is not applicable for the?
?arguments (String). The inferred type String is not a valid substitute for the bounded?
?parameter <T extends Animal>
?
?這時候只能????????????????b.printInfo2(new Dog());
?? ??? ??? ?????????????????????????b.printInfo2(new Cat());
?? ??? ??? ?????????????????????????b.printInfo2(new Animal());
也就是我們之前講的對泛型<T>的限制,放在泛型方法里面是同樣適用的
?
?運行結果
?靜態方法:
我們比如把
public <T extends Animal> void printInfo2(T t)變成
public static <T extends Animal> void printInfo2(T t)
警告:? ? ? ? 第71行,第72行,第73行
The static method printInfo2(Dog) from the type B should be accessed in a static way
?
?出現這種情況,完全可以?? ?B.printInfo2(new Dog());
靜態方法就是要求我們這樣做
不用非得實例化,類的名字加上方法名,做一個靜態的調用
靜態方法不能使用其所在類的類型參數:
因為靜態方法本身就是和這個類脫離的
注意:這個T會被重置,編譯的時候
第33行的T? ? ? public <T> void printInfo(T t)?????????到最后會變成object
第44行的T? ? ?public static <T extends Animal> void printInfo2(T t)??????????????????到最后會變成Animal
?
?注意:不管你是泛型方法還是普通方法,你不能搞一摸一樣的
Duplicate method print1(int) in type B
?
總結
以上是生活随笔為你收集整理的Java:高级之泛型概念引入,泛型可以设置多个类型参数,泛型继承和泛型接口实现,限制泛型可用类型,泛型通配的方式,泛型方法,泛型方法限制泛型可用类型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java:异常的概念及分类、异常的处理、
- 下一篇: 计提商誉减值是什么