泛型

一.泛型是什么

?? 避免強轉、在編譯時檢查出來傳進去的類型對不對、利于程序擴展。

二.各種泛型定義及使用

1.泛型類型定義及使用

//定義  
class Point<T>{// 此處可以隨便寫標識符號   
    private T x ;        
    private T y ;        
    public void setX(T x){//作為參數  
        this.x = x ;  
    }  
    public void setY(T y){  
        this.y = y ;  
    }  
    public T getX(){//作為返回值  
        return this.x ;  
    }  
    public T getY(){  
        return this.y ;  
    }  
};  
//IntegerPoint使用  
Point<Integer> p = new Point<Integer>() ;   
p.setX(new Integer(100)) ;   
System.out.println(p.getX());    
  
//FloatPoint使用  
Point<Float> p = new Point<Float>() ;   
p.setX(new Float(100.12f)) ;   
System.out.println(p.getX());  

??(1)定義泛型:Point<T>
首先,大家可以看到Point<T>,即在類名后面加一個尖括號,括號里是一個大寫字母。這里寫的是T,其實這個字母可以是任何大寫字母,大家這里先記著,可以是任何大寫字母,意義是相同的。
??(2)類中使用泛型
這個T表示派生自Object類的任何類,比如String,Integer,Double等等。這里要注意的是,T一定是派生于Object類的。為方便起見,大家可以在這里把T當成String,即String在類中怎么用,那T在類中就可以怎么用!
??(3)使用泛型類
在泛型類構造的時候,在類名后面加上<想傳入的類型>

2、多泛型變量定義及字母規范

??(1)多泛型變量定義
<T,U,A,B,C....>相加幾個就加幾個
??(2)、字母規范
任意一個大寫字母都可以。他們的意義是完全相同的,但為了提高可讀性,大家還是用有意義的字母比較好,一般來講,在不同的情境下使用的字母意義如下:

  • E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
  • K,V — Key,Value,代表Map的鍵值對
  • N — Number,數字
  • T — Type,類型,如String,Integer等等

3、泛型接口定義及使用

泛型接口和泛型類一樣:

public interface Infor<T> {
      T getVar();

}

但是在使用時候有以下兩種情況;

  • 使用方法一:非泛型類
class InforImpl implements  Infor<String>{


    @Override
    public String getVar() {
        return null;
    }
}

??先要認清楚一點InforImpl 不是一個泛型類,因為它后面沒有<T>!!!這里我們將Infor<>填充了String類型,getVar()的返回值也變成了String。

  • 使用方法二:泛型類
class InforImpl<T> implements  Infor<T>{


    @Override
    public T getVar() {
        return null;
    }
}

??我們構造了一個泛型類InforImpl<T>,然后把泛型變量T 傳給了Infor<T>,說明接口和泛型類使用的是同一個泛型變量。在使用的過程中:

 這里傳入想要的類型<String>
 InforImpl<String> infor = new InforImpl();
 String var = infor.getVar();

4、泛型函數定義及使用

上面提到了類和接口的泛型使用,下面看看怎么單獨在一個函數中使用泛型:

public class StaticFans {

    public static <T> void staticMethod(T t) {

        System.out.println(t.toString());

    }

    public <T> void method(T t) {

        System.out.println(t.toString());
    }


}

??上面的寫法和平時的方法差不多,唯一的區別就是在返回值(Void)前面加上<T>來表示泛型變量.
使用方法如下:

   &ensp;&ensp;     可以像普通方法一樣,直接傳值,任何值都可以(但必須是派生自Object類的類型,比如String,Integer等),函數會在內部根據傳進去的參數來識別當前T的類別
        StaticFans.staticMethod("String類型");
        StaticFans.staticMethod(666666);//整型
      先創建類的實例,然后調用泛型函數。
        new StaticFans().method("String類型");
        new StaticFans().method(666666);
  • 進階:返回值中存在泛型
public static <T> List<T> parseArray(String response,Class<T> object){  
    List<T> modelList = JSON.parseArray(response, object);  
    return modelList;  
}

函數的返回值是List<T>類型

5、其它用法:Class<T>類傳遞

有時,我們在Gson解析對象時,代碼一般這樣寫

public static List<SuccessModel> parseArray(String response){  
    List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);  
    return modelList;  
}

??如果只有一兩個類還行,如果需要很多,這么寫豈不是很麻煩,parseArray里面我們需要一個SuccessModel.class,這時候就要用上Class<T>,使用Class<T>傳遞泛型類Class對象。

public static <T> List<T> parseArray(String response,Class<T> object){  
    List<T> modelList = JSON.parseArray(response, object);  
    return modelList;  
}

??注意到,我們用的Class<T> object來傳遞類的class對象,即我們上面提到的SuccessModel.class。
--------------------- 下面是進階部分-------------------
-------被溫水煮慣了,夢想的東西總是不敢于嘗試,失敗了又怎樣,最多從頭來過。--------

一 、類型綁定:extends

??有時候,你會希望泛型類只能是某一部分,,比如操作數據的時候,你希望是Number或者是它的子類,這個想法就是給泛型參數增加一個范圍

<T extends BoundingType>  

??T 表示BoundingType的子類型,T和BoundingType都可以是類,也可以是接口。extends表示子類型,不等同于繼承。注意:::這里的extends不是繼承里的extends,兩者根本沒有任何關聯。
先設置一個基類

class Fruit {  
    private String name;  
  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  

再寫個泛型函數來取名字

public static <T extends Fruit> String getFruitName(T t){  
    return t.getName();  
} 

??這里泛型函數的用法就出來了,由于我們已經水果都會繼承Fruit類,所以我們利用<T extends Fruit>就可以限定填充的變量必須派生字Fruit的子類。一來,在T中,我們可以利用Fruit中方法和函數;二來,如果用戶填充進去的類沒有派生自Fruit,那編譯器就會報錯。
然后,我們新建兩個類,派生自Fruit,并填充進去它們自己的名字:

class Banana extends Fruit{  
    public Banana(){  
        setName("bababa");  
    }  
}  
class Apple extends Fruit{  
    public Apple(){  
        setName("apple");  
    }  
}  

最后調用:

String name_1 = getFruitName(new Banana());  
String name_2 = getFruitName(new Apple());  
Log.d(TAG,name_1);  
Log.d(TAG,name_2);  

二、通配符

1.引入
public class Point<T> {

    private T x;
    private T y;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}

使用情況:

Point<Integer> integerPoint = new Point<Integer>(3,3);  
…………  
Point<Float> floatPoint = new Point<Float>(4.3f,4.3f);  
…………  
Point<Double> doublePoint = new Point<Double>(4.3d,4.90d);  
…………  
Point<Long> longPoint = new Point<Long>(12l,23l);  

在這段代碼中,我們使用Point<T>生成了四個實例:integerPoint,floatPoint,doublePoint和longPoint;
??在這里,我們生成四個實例,就得想四個名字。如果我們想生成十個不同類型的實例呢?那不得想十個名字。
??光想名字就是個事,(其實我并不覺得想名字是個什么大事…… T _ T ,沒辦法,想不出更好的例子了…… )
??那有沒有一種方法,生成一個變量,可以將不同類型的實例賦值給它嗎?---?來了

2.無邊界通配符: ?

如果我們用?,可以這樣實現

Point<?> point;  
  
point = new Point<Integer>(3,3);  
point = new Point<Float>(4.3f,4.3f);  
point = new Point<Double>(4.3d,4.90d);  
point = new Point<Long>(12l,23l);  

? 就是無邊界通配符,可以代表任意的類

?和T的區別

?和T 沒有任何的聯系?。?!
?和T 沒有任何的聯系?。。?br> ?和T 沒有任何的聯系?。。?br> ??泛型T不能在代碼用于創建變量,只能在類、接口、函數中聲明以后,才能使用。
而無界通配符?只能用于填充泛型變量T,它是用來填充T的!??!只是填充方式的一種。

3、通配符?的extends綁定

從上面我們可以知道通配符?可以代表任意類型,但跟泛型一樣,如果不加以限定,在后期的使用中編譯器可能不會報錯。所以我們同樣,要對?加以限定。
綁定的形式,同樣是通過extends關鍵字,意義和使用方法都用泛型變量一致。
同樣,以我們上面的Point<T>泛型類為例,因為Point在實例意義中,其中的值是數值才有意義,所以將泛型變量T填充為Object類型、String類型等都是不正確的。
所以我們要對Point<?> point加以限定:只有數值類型才能賦值給point;
我們把代碼改成下面的方式:

        Point<? extends  Number> point;
        point=new Point<Number>();
        point=new Point<Integer>();
        point=new Point<String>();//有錯誤
        point=new Point<Object>();//有錯誤

??我們給通配符加上限定:Point<? extends Number> point;此時,最后兩行,當T填充為String和Object,point就會報錯。雖然指派的是派生自Number的任意類型, new Point<Number>();也是可以成功賦值的,說明包括邊界本身。
??再強調一遍:無邊界通配符只是泛型T的填充方式,給它加上限定,只是限定了賦值給它的實體類。

注意:利用<? extends Number>定義的變量,只可取其中的值,不可修改
        Point<? extends Number> point;
        point = new Point<Integer>(3, 3);
        Number i = point.getX();
        point.setX(1);//這里會報錯

為什么getX()可以,而setX()不行?
??首先,point類型是由Point<? extends Number>決定的,并不會因為 point = new Point<Integer>(3, 3)而改變類型,假設如果point類型隨著 point = new Point<Integer>(3, 3)改變,那么point = new Point<Long>(12l,23l)就會報錯,正因為point類型始終是<? extends Number>,因為能被各種實例賦值。打個比方:Point類就好比學生,point是學生1,此時point類型還是學生,而不是學生1.
??然后,正因為point類型<? extends Number>,這是一個什么類型?這其實是一個未知類型Integer、long、double。。。怎么可能給一個未知類型去賦值?顯然是不合理的。
??最后,當我們去getX()的時候,取得類型可能是Integer、long。。雖然類型不確定,但它一定是Number的子類,也就是說,編譯器只要能確定通配符的類型,就會允許,如果無法確定通配符的類型,就會報錯。

4、通配符?的super綁定

?? 如果說< ? extends XXX> 指的是填充派生于XXX的子類,那么<? super XXX> 指的是任意XXX的父類。

class CEO extends Manager {  
}  
  
class Manager extends Employee {  
}  
  
class Employee {  
}  
       List<? super Manager> list;
        list = new ArrayList<Employee>();
        list = new ArrayList<Manager>();
        list = new ArrayList<CEO>();//這句話報錯

因為CEO已經不是Manager的父類了,所以編譯會報錯。super的關鍵字也是包括邊界的。

super通配符實例內容:能存不能取
       List<? super Manager> list=new ArrayList<>();

        list.add(new CEO());
        list.add(new Manager());
        list.add(new Employee());//這句話報錯

為什么前兩個行,最后一個不行。這里可以這么理解:父類的引用可以指向子類對象。假設?是Manager,CEO是Manager的子類,最后都可以強轉成Manager。但是Manager是Employee 的子類,所以Employee 不行。這就好比我聲明的是一個Dog對象,我們在add的時候不能new 一個Animal進去。
?? 我們再來看看?。?/p>

       Object object=list.get(0);
        Employee employee=list.get(0); //報錯
        Manager manager=list.get(0);//報錯
        CEO ceo=list.get(0);//報錯

我們填充的是Manager的父類,假設是Employee 或者是Object,取得的值肯定是Object的子類,所以第一個肯定沒有問題。我們無法判斷我們取得值是什么類型,所以都是報錯的。但取出一個Object也沒有什么意思,所以,我們認為super能存不能取。

5、通配符?總結
  • 如果你想從一個數據類型里獲取數據,使用 ? extends 通配符(能取不能存)
  • 如果你想把對象寫入一個數據結構里,使用 ? super 通配符(能存不能?。?/li>
  • 如果你既想存,又想取,那就別用通配符。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容

  • 泛型的定義及使用 1. 定義泛型: 2. 類中使用泛型 3. 使用泛型類 4. 使用泛型的優勢? 多泛型變量的定義...
    xue57233閱讀 442評論 0 1
  • 我們知道,使用變量之前要定義,定義一個變量時必須要指明它的數據類型,什么樣的數據類型賦給什么樣的值。 假如我們現在...
    今晚打肉山閱讀 998評論 0 1
  • 開發人員在使用泛型的時候,很容易根據自己的直覺而犯一些錯誤。比如一個方法如果接收List作為形式參數,那么如果嘗試...
    時待吾閱讀 1,070評論 0 3
  • 第8章 泛型 通常情況的類和函數,我們只需要使用具體的類型即可:要么是基本類型,要么是自定義的類。但是在集合類的場...
    光劍書架上的書閱讀 2,154評論 6 10