Java泛型

為什么需要泛型

  • 泛型利于代碼重用。比如實現(xiàn)針對某一種具體數(shù)據(jù)類型的功能,將具體數(shù)據(jù)類型替換為泛型枢希,則可以實現(xiàn)為針對多種數(shù)據(jù)類型的功能,極大的提高了功能的復用性朱沃。

  • 類型安全苞轿,讓編譯器幫助我們進行類型檢查茅诱。指定泛型中的類型,讓java編譯器幫助我們檢查類型以及類型轉(zhuǎn)換搬卒,不再需要我們自己進行類型判斷及強制裝換瑟俭。

泛型的使用

//動物類
public class Animal {
    public void eat(){
        System.out.println("動物吃");
    }
}

//貓類
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("貓吃東西");
    }

    public void cry(){
        System.out.println("喵喵叫");
    }
}

//卡菲貓
public class Garfield extends Cat {
}
//泛型類的使用,這種泛型使用方式會導致類型被擦除為所有類的父類Object
//這意味著你在運行期會丟失原本類型的所有信息契邀,無法使用原本類相關(guān)的任何屬性和方法
public class GenericClass<T> {
    //泛型擦除摆寄,查看字節(jié)碼data的類型為 Ljava/lang/Object; data
    private T data;

    GenericClass(T data){
        this.data = data;
    }

    public T getData(){
        return data;
    }

    public void setData(T data){
        this.data = data;
    }
}

//使用了限制的泛型,通過這種方式可以一定程度上彌補第一種方式的弊端
//因為泛型類型被擦除為指定父類坯门,這樣可以使用父類的方法
public class GenericExtendsClass<T extends Animal> {
    //泛型擦除為Animal微饥,查看字節(jié)碼data的類型為 L easycode/Animal; data
    private T data;

    GenericExtendsClass(T data){
        this.data = data;
    }

    public T getData(){
        return data;
    }

    public void setData(T data){
        this.data = data;
    }

    public void doOtherSomething(){
        //調(diào)用了Animal特有的方法
        data.eat();
    }
}

//泛型方法的使用,只能定義靜態(tài)泛型方法
private static <T> T genericAdd(T a, T b) {
        System.out.println(a + "+" + b + "="+a+b);
        return a;
}

java泛型的本質(zhì)

c++的泛型是根據(jù)模板類生成不同的類達到泛型的目的古戴,但是java的泛型并不是如此的欠橘,java是通過泛型擦除的方式來實現(xiàn)泛型,所以java的泛型也被稱為偽泛型现恼,即只存在于編譯期的泛型肃续,在運行階段找不到泛型信息。

所以叉袍,可以將java的泛型當做是一種類型檢查的手段始锚,而不要像c++一樣將泛型作為實際的類型來使用。

Java泛型中的約束

  • 泛型不能實例化畦韭。因為java的泛型擦除疼蛾,所以在運行期不知道泛型信息,自然也就無法實例化艺配。

  • 類或接口的靜態(tài)域中不能引用泛型類型變量察郁,但是可以定義靜態(tài)泛型方法

  • 基本數(shù)據(jù)類型無法作為泛型類型转唉。因為泛型擦除其實就是將子類賦值給父類皮钠,所以基本數(shù)據(jù)類型無法作為泛型類型。

  • 無法使用 instanceof 關(guān)鍵字或者 ==運算符 判斷泛型類的類型赠法,需要借助別的方法(Type類型)麦轰,但是對原生數(shù)據(jù)類型無影響

  • 泛型類的原生數(shù)據(jù)類型與所傳遞的泛型無關(guān),無論傳遞什么類型砖织,原生類都是一樣的

  • 泛型數(shù)組可以聲明但無法實例化款侵。原因同泛型不能實例化,但是可以實例化非泛型數(shù)組侧纯,然后通過定義變量的泛型來進行類型檢查新锈。

    //實例化泛型數(shù)組,編譯不通過
    //ArrayList<String>[] listArray = new ArrayList<String>[5];
            
    //實例化非泛型數(shù)組眶熬,編譯通過
    //通過給listArray添加泛型定義來進行泛型檢查
    ArrayList<String>[] listArray = new ArrayList[5];
            
    //編譯不通過
    //listArray[0] = new ArrayList<Integer>();
            
    //編譯通過
    listArray[0] = new ArrayList<String>();
    
  • 泛型類不能繼承Exception或者Throwable

  • 不能捕獲泛型類型限定的異常妹笆,只能將泛型類型限定的異常拋出

泛型通配符

泛型通配符主要用于一些參數(shù)的接收或返回块请。

通配符類型

  • <? extends Parent>:指定了泛型類的上界,一般用于獲取元素即get first拳缠,因為可以肯定元素為Parent的某一個子類或Patent墩新,但是具體是哪一個類型無法確定,所以無法添加窟坐,只能獲取海渊。

  • <? super Child>:指定了泛型的下界,一般用于添加元素即put first狸涌,因為可以肯定元素為Child的父類切省,所以可以添加Child類型或者Child的子類,都是滿足多態(tài)存儲的帕胆。

  • <?>:等價于<? extends Object>朝捆,即沒有限制的泛型類型。
    <?>比如 List<?>一般作為參數(shù)來接收外部的集合懒豹,或者返回一個不知道具體元素類型的集合芙盘。因為<?>代表未知類型,所以不允許往里面添加元素脸秽,只能取出來儒老。比如

    public List<?> getList(){
      return new ArrayList<String>();
    }
    
        //測試 extends 和 super
        List<Animal> animal = new ArrayList<Animal>();
        ArrayList<Cat> cat = new ArrayList<>();
        ArrayList<Garfield> garfield = new ArrayList<>();


        animal.add(new Animal());
        cat.add(new Cat());
        garfield.add(new Garfield());

        //測試賦值操作
        //編譯出錯,Animal是Cat的父類记餐,不能通過泛型檢測
//        List<? extends Cat> extendsCatFromAnimal = animal;
        List<? super Cat> superCatFromAnimal = animal;

        List<? extends Cat> extendsCatFromCat = cat;
        List<? super Cat> superCatFromCat = cat;

        List<? extends Cat> extendsCatFromCarfield = garfield;
        //編譯出錯 Garfield是 Cat的子類驮樊,不能通過泛型檢測
//        List<? super Cat> superCatFromCarfield = carfield;


        //測試add方法
            //編譯出錯,extends只能獲取元素而不能添加元素
//        extendsCatFromCat.add(new Animal());
//        extendsCatFromCat.add(new Cat());
//        extendsCatFromCat.add(new Garfield());

            //編譯出錯片酝,可以添加Cat及其子類囚衔,但是不能添加其父類Animal
//        superCatFromCat.add(new Animal());
        superCatFromCat.add(new Cat());
        superCatFromCat.add(new Garfield());


        //測試get方法
        Object catExtends2 = extendsCatFromCat.get(0);
        Cat catExtends1 = extendsCatFromCat.get(0);
//        Garfield garfield1 = extendsCatFromCarfield.get(0);

        //可以確定Object是所有類的父類
        Object catSuper1 = superCatFromCat.get(0);
        Object catSuper2 = superCatFromAnimal.get(0);

如何獲取泛型的參數(shù)類型

獲取泛型的類型參數(shù)類型需要用到Type接口以及它的子接口。
獲取泛型的類型參數(shù)類型需要用到Type接口以及它的子接口雕沿。

Type類型的子接口有ParameterizedType练湿、GenericArrayType、TypeVariable审轮、WildcardType和Class實現(xiàn)類肥哎。

public interface ParameterizedType extends Type {
    //private List<String> list;
    //獲取<>中的數(shù)據(jù)類型,即String
    Type[] getActualTypeArguments();
    //獲取<>前面的實際類型疾渣,即List
    Type getRawType();
    //如果這個類型是某個類型的所屬篡诽,獲得這個所有者類型,否則返回null
    Type getOwnerType();
}
public interface GenericArrayType extends Type {
    // T[]
   //獲取數(shù)組元素類型榴捡,即 T
    Type getGenericComponentType();
}

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
    //private T t;
    //獲取泛型的上界杈女,可以通過extends通配符指定,默認是Object
    Type[] getBounds();
    //獲取聲明該類型變量實體,即該變量所在的類、方法等
    D getGenericDeclaration();
    //獲得名稱碧信,即K、V街夭、E
    String getName();
}
public interface WildcardType extends Type {
    //獲取泛型表達式上界
    Type[] getUpperBounds();
    //獲取泛型表達式下界
    Type[] getLowerBounds();
   
}

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {

Type接口時是Java編程語言中所有類型的公共高級接口砰碴,其中的所有類型如下所示:

  • 原始類型(一般的java類包括枚舉、注解板丽、非泛型數(shù)組)呈枉,對應 Class 類

  • 參數(shù)化類型(List<String>、Map<String, Object>這種形式)埃碱,對應 ParameterizedType 接口

  • 數(shù)組類型(T[])猖辫,對應 GenericArrayType 接口

  • 類型變量(T),對應 TypeVariable 接口

  • 基本類型砚殿,對應Class對象

舉個例子(獲取泛型類型)

public class GenericTest {
    static class Example{
        //參數(shù)化類型
        private List<String> list;
    }

    @Test
    public void test() throws Exception{
        Class<Example> exampleClass = Example.class;
        Field field = exampleClass.getDeclaredField("list");

        Type type = field.getGenericType();
        Assert.assertTrue(type instanceof ParameterizedType);

        ParameterizedType parameterizedType = (ParameterizedType)type;
        Assert.assertEquals(String.class ,parameterizedType.getActualTypeArguments()[0]);
        Assert.assertEquals(List.class, parameterizedType.getRawType());
    }
}

參考如下文章:
Java泛型
Type類型常用API介紹
Type類型Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啃憎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子似炎,更是在濱河造成了極大的恐慌辛萍,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羡藐,死亡現(xiàn)場離奇詭異贩毕,居然都是意外死亡,警方通過查閱死者的電腦和手機仆嗦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門辉阶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瘩扼,你說我怎么就攤上這事谆甜。” “怎么了邢隧?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵店印,是天一觀的道長。 經(jīng)常有香客問我倒慧,道長按摘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任纫谅,我火速辦了婚禮炫贤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘付秕。我一直安慰自己兰珍,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布询吴。 她就那樣靜靜地躺著掠河,像睡著了一般亮元。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唠摹,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天爆捞,我揣著相機與錄音,去河邊找鬼勾拉。 笑死煮甥,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的藕赞。 我是一名探鬼主播成肘,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼斧蜕!你這毒婦竟也來了双霍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤批销,失蹤者是張志新(化名)和其女友劉穎店煞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體风钻,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡顷蟀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了骡技。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸣个。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖布朦,靈堂內(nèi)的尸體忽然破棺而出囤萤,到底是詐尸還是另有隱情,我是刑警寧澤是趴,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布涛舍,位于F島的核電站,受9級特大地震影響唆途,放射性物質(zhì)發(fā)生泄漏富雅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一肛搬、第九天 我趴在偏房一處隱蔽的房頂上張望没佑。 院中可真熱鬧,春花似錦温赔、人聲如沸蛤奢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啤贩。三九已至待秃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間痹屹,已是汗流浹背锥余。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痢掠,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓嘲恍,卻偏偏與公主長得像足画,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子佃牛,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一俘侠、概述 Java開發(fā)經(jīng)常會用到泛型象缀,常用的List、Map都用到了爷速,泛型在Java中有很重要的地位央星,被廣泛應用于...
    碼農(nóng)翻身記閱讀 227評論 0 1
  • ?? 本文已歸檔到:「javacore」?? 本文中的示例代碼已歸檔到:「javacore」 1. 為什么需要泛型 J...
    靜默虛空閱讀 169評論 1 1
  • 一、學習目標 1.泛型的作用和定義 2.泛型的基本使用 3.泛型中的通配符 4.泛型擦除 5.泛型中的約束和局限 ...
    Heezier閱讀 299評論 0 1
  • Java 泛型 1惫东、泛型的精髓是什么 2莉给、泛型方法如何使用 概述: 泛型在java中具有重要地位,在面向?qū)ο缶幊棠?..
    SeanJX閱讀 481評論 0 0
  • 泛型的定義 Java 泛型(generics)是 JDK1. 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢...
    安仔夏天勤奮閱讀 183評論 0 2