java泛型

泛型的本質是為了參數(shù)化類型研乒。也就是說在泛型使用過程中乏盐,操作的數(shù)據(jù)類型被指定為一個參數(shù)佳窑,這種參數(shù)類型可以用在類、接口和方法中父能,分別被稱為泛型類神凑、泛型接口、泛型方法

  • 泛型只在編譯過程中有效, 在運行階段失效;
  • 泛型的類型參數(shù)只能是類類型法竞,不能是簡單類型

定義格式

class 類名稱 <泛型標識:可以隨便寫任意標識號耙厚,標識指定的泛型的類型{
  private 泛型標識 /*(成員變量類型)*/ var; 
  .....

  }
}

泛型類

//此處T可以隨便寫為任意標識强挫,常見的如T、E薛躬、K俯渤、V等形式的參數(shù)常用于表示泛型
//在實例化泛型類時,必須指定T的具體類型(注意: 這里T代表具體類型,非簡單類型)
public class Generic<T>{ 
    //key這個成員變量的類型為T,T的類型由外部指定  
    private T key;

    public Generic(T key) { //泛型構造方法形參key的類型也為T型宝,T的類型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值類型為T八匠,T的類型由外部指定
        return key;
    }
}
//泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型
//傳入的實參類型需與泛型的類型參數(shù)類型相同趴酣,即為Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);

//傳入的實參類型需與泛型的類型參數(shù)類型相同梨树,即為String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型測試","key is " + genericInteger.getKey());
Log.d("泛型測試","key is " + genericString.getKey());
12-27 09:20:04.432 13063-13063/? D/泛型測試: key is 123456
12-27 09:20:04.432 13063-13063/? D/泛型測試: key is key_vlaue

在實例化時不使用泛型實參的話, 則可以定義為任何類型

Generic generic = new Generic("111111");
Generic generic1 = new Generic(4444);
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);

Log.d("泛型測試","key is " + generic.getKey());
Log.d("泛型測試","key is " + generic1.getKey());
Log.d("泛型測試","key is " + generic2.getKey());
Log.d("泛型測試","key is " + generic3.getKey());

輸出結果:

D/泛型測試: key is 111111
D/泛型測試: key is 4444
D/泛型測試: key is 55.55
D/泛型測試: key is false

泛型接口

  • 泛型接口常被用在各種類的生產器中;
  • 定義:
//定義一個泛型接口
public interface Generator<T> {
    public T next();
}

使用:

/**
 * 傳入泛型實參時:
 * 定義一個生產器實現(xiàn)這個接口,雖然我們只創(chuàng)建了一個泛型接口Generator<T>
 * 但是我們可以為T傳入無數(shù)個實參,形成無數(shù)種類型的Generator接口岖寞。
 * 在實現(xiàn)類實現(xiàn)泛型接口時抡四,如已將泛型類型傳入實參類型,則所有使用泛型的地方都要替換成傳入的實參類型
 * 即:Generator<T>仗谆,public T next();中的的T都要替換成傳入的String類型指巡。
 */
public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

通配符

Generic<Integer> gen1 = new Generic<Integer>(12345);
Generic<String> gen2 =  Generic<String>("Hello liangxifeng");
showKeyValue1(gen2);
public void showKeyValue1(Generic<Integer> obj){
    System.out.println("泛型測試key value is " + obj.getKey());
}

以上運行會報錯:

// showKeyValue這個方法編譯器會為我們報錯:Generic<java.lang.String> 
// cannot be applied to Generic<java.lang.Integer>
// showKeyValue(gInteger);

使用統(tǒng)配符 ?解決

public void showKeyValue1(Generic<?> obj){
    System.out.println("泛型測試key value is " + obj.getKey());
}

以上程序在運行就不會報錯了

此處’?’是類型實參隶垮,而不是類型形參 藻雪!再直白點的意思就是,此處的狸吞?和Number勉耀、String、Integer一樣都是一種實際的類型蹋偏,可以把便斥?看成所有類型的父類。是一種真實的類型

泛型方法

  • 泛型類暖侨,是在實例化類的時候指明泛型的具體類型椭住;
  • 泛型方法崇渗,是在調用方法的時候指明泛型的具體類型 字逗。
  • 必須使用 public <T>方式聲明才叫泛型方法, 新聲明的泛型方法中的T與所屬類中聲明的泛型T Class<T>可以不一致;
/**
 * 泛型方法的基本介紹
 * @param tClass 傳入的泛型實參
 * @return T 返回值為T類型
 * 說明:
 *     1)public 與 返回值中間<T>非常重要,可以理解為聲明此方法為泛型方法宅广。
 *     2)只有聲明了<T>的方法才是泛型方法葫掉,泛型類中的使用了泛型的成員方法并不是泛型方法。
 *     3)<T>表明該方法將使用泛型類型T跟狱,此時才可以在方法中使用泛型類型T俭厚。
 *     4)與泛型類的定義一樣,此處T可以隨便寫為任意標識驶臊,常見的如T挪挤、E叼丑、K、V等形式的參數(shù)常用于表示泛型扛门。
 */
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
  IllegalAccessException{
        T instance = tClass.newInstance();
        return instance;
}

詳細說明

public class GenericTest {
   //這個類是個泛型類鸠信,在上面已經介紹過
   public class Generic<T>{     
        private T key;

        public Generic(T key) {
            this.key = key;
        }

        //我想說的其實是這個,雖然在方法中使用了泛型论寨,但是這并不是一個泛型方法星立。
        //這只是類中一個普通的成員方法,只不過他的返回值是在聲明泛型類已經聲明過的泛型葬凳。
        //所以在這個方法中才可以繼續(xù)使用 T 這個泛型绰垂。
        public T getKey(){
            return key;
        }

        /**
         * 這個方法顯然是有問題的,在編譯器會給我們提示這樣的錯誤信息"cannot reslove symbol E"
         * 因為在類的聲明中并未聲明泛型E火焰,所以在使用E做形參和返回值類型時劲装,編譯器會無法識別。
        public E setKey(E key){
             this.key = keu
        }
        */
    }

    /** 
     * 這才是一個真正的泛型方法昌简。
     * 首先在public與返回值之間的<T>必不可少酱畅,這表明這是一個泛型方法,并且聲明了一個泛型T
     * 這個T可以出現(xiàn)在這個泛型方法的任意位置.
     * 泛型的數(shù)量也可以為任意多個 
     *    如:public <T,K> K showKeyName(Generic<T> container){
     *        ...
     *        }
     */
    public <T> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        //當然這個例子舉的不太合適江场,只是為了說明泛型方法的特性纺酸。
        T test = container.getKey();
        return test;
    }

    //這也不是一個泛型方法,這就是一個普通的方法址否,只是使用了Generic<Number>這個泛型類做形參而已餐蔬。
    public void showKeyValue1(Generic<Number> obj){
        Log.d("泛型測試","key value is " + obj.getKey());
    }

    //這也不是一個泛型方法,這也是一個普通的方法佑附,只不過使用了泛型通配符?
    //同時這也印證了泛型通配符章節(jié)所描述的樊诺,?是一種類型實參,可以看做為Number等所有類的父類
    public void showKeyValue2(Generic<?> obj){
        Log.d("泛型測試","key value is " + obj.getKey());
    }

     /**
     * 這個方法是有問題的音同,編譯器會為我們提示錯誤信息:"UnKnown class 'E' "
     * 雖然我們聲明了<T>,也表明了這是一個可以處理泛型的類型的泛型方法词爬。
     * 但是只聲明了泛型類型T,并未聲明泛型類型E权均,因此編譯器并不知道該如何處理E這個類型顿膨。
    public <T> T showKeyName(Generic<E> container){
        ...
    }  
    */

    /**
     * 這個方法也是有問題的,編譯器會為我們提示錯誤信息:"UnKnown class 'T' "
     * 對于編譯器來說T這個類型并未項目中聲明過叽赊,因此編譯也不知道該如何編譯這個類恋沃。
     * 所以這也不是一個正確的泛型方法聲明。
    public void showkey(T genericObj){

    }
    */

    public static void main(String[] args) {


    }
}
public class GenericFruit {
    class Fruit{
        @Override
        public String toString() {
            return "fruit";
        }
    }

    class Apple extends Fruit{
        @Override
        public String toString() {
            return "apple";
        }
    }

    class Person{
        @Override
        public String toString() {
            return "Person";
        }
    }

    class GenerateTest<T>{
        public void show_1(T t){
            System.out.println(t.toString());
        }

        //在泛型類中聲明了一個泛型方法必指,使用泛型E囊咏,這種泛型E可以為任意類型。可以類型與T相同梅割,也可以不同霜第。
        //由于泛型方法在聲明的時候會聲明泛型<E>,因此即使在泛型類中并未聲明泛型户辞,編譯器也能夠正確識別泛型方法中識別的泛型庶诡。
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        //在泛型類中聲明了一個泛型方法,使用泛型T咆课,注意這個T是一種全新的類型末誓,可以與泛型類中聲明的T不是同一種類型。
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        Person person = new Person();

        GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
        //apple是Fruit的子類书蚪,所以這里可以
        generateTest.show_1(apple);
        //編譯器會報錯喇澡,因為泛型類型實參指定的是Fruit,而傳入的實參類是Person
        //generateTest.show_1(person);

        //使用這兩個方法都可以成功
        generateTest.show_2(apple);
        generateTest.show_2(person);

        //使用這兩個方法也都可以成功
        generateTest.show_3(apple);
        generateTest.show_3(person);
    }
}

泛型方法與可變參數(shù)

public <T> void printMsg( T... args){
    for(T t : args){
        Log.d("泛型測試","t is " + t);
    }
}

輸出:

printMsg("111",222,"aaaa","2323.4",55.55);

靜態(tài)方法與泛型

  • 靜態(tài)方法無法訪問類上定義的泛型殊校;
  • 如果靜態(tài)方法操作的引用數(shù)據(jù)類型不確定的時候晴玖,必須要將泛型定義在方法上
public class StaticGenerator<T> {
    ....
    ....
    /**
     * 如果在類中定義使用泛型的靜態(tài)方法,需要添加額外的泛型聲明(將這個方法定義成泛型方法)
     * 即使靜態(tài)方法要使用泛型類中已經聲明過的泛型也不可以为流。
     * 如:public static void show(T t){..},此時編譯器會提示錯誤信息:
          "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show(T t){

    }
}

泛型方法的總結

  • 無論何時呕屎,如果你能做到,你就該盡量使用泛型方法敬察。
  • 也就是說秀睛,如果使用泛型方法將整個類泛型化,那么就應該使用泛型方法莲祸。
  • 另外對于一個static的方法而已蹂安,無法訪問泛型類型的參數(shù)。所以如果static方法要使用泛型能力锐帜,就必須使其成為泛型方法田盈。

泛型上下邊界

  • 在使用泛型的時候,我們還可以為傳入的泛型類型實參進行上下邊界的限制缴阎,如:類型實參只準傳入某種類型的父類或某種類型的子類允瞧。
  • 為泛型添加上邊界,即傳入的類型實參必須是指定類型的子類型
public void showKeyValue1(Generic<? extends Number> obj){
    Log.d("泛型測試","key value is " + obj.getKey());
}
Generic<String> generic1 = new Generic<String>("11111");
Generic<Integer> generic2 = new Generic<Integer>(2222);
Generic<Float> generic3 = new Generic<Float>(2.4f);
Generic<Double> generic4 = new Generic<Double>(2.56);

//這一行代碼編譯器會提示錯誤蛮拔,因為String類型并不是Number類型的子類
//showKeyValue1(generic1);

showKeyValue1(generic2);
showKeyValue1(generic3);
showKeyValue1(generic4);

我們修改一下泛型的定義

public class Generic<T extends Number>{
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}
//這一行代碼也會報錯述暂,因為String不是Number的子類
Generic<String> generic1 = new Generic<String>("11111");
  • 泛型的上下邊界添加,必須與泛型的聲明在一起
//在泛型方法中添加上下邊界限制的時候语泽,必須在權限聲明與返回值之間的<T>上添加上下邊界贸典,即在泛型聲明的時候添加
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會報錯:"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
    System.out.println("container key :" + container.getKey());
    T test = container.getKey();
    return test;
}

泛型數(shù)組

  • 不能創(chuàng)建一個確切的泛型類型的數(shù)組, 但是使用通配符 ?使可以的
//錯誤寫法
List<String>[] ls = new ArrayList<String>[10];
//正確寫法
List<?>[] lsa = new ArrayList<?>[10];
//正確寫法
List<String>[] lsb = new ArrayList[10];

參考文章: http://m.blog.csdn.net/s10461/article/details/53941091

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市西饵,隨后出現(xiàn)的幾起案子酝掩,更是在濱河造成了極大的恐慌,老刑警劉巖眷柔,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件期虾,死亡現(xiàn)場離奇詭異,居然都是意外死亡驯嘱,警方通過查閱死者的電腦和手機镶苞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鞠评,“玉大人茂蚓,你說我怎么就攤上這事√昊希” “怎么了聋涨?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長负乡。 經常有香客問我牍白,道長,這世上最難降的妖魔是什么抖棘? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任淹朋,我火速辦了婚禮,結果婚禮上钉答,老公的妹妹穿的比我還像新娘础芍。我一直安慰自己,他們只是感情好数尿,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布仑性。 她就那樣靜靜地躺著,像睡著了一般右蹦。 火紅的嫁衣襯著肌膚如雪诊杆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天何陆,我揣著相機與錄音晨汹,去河邊找鬼。 笑死贷盲,一個胖子當著我的面吹牛淘这,可吹牛的內容都是我干的剥扣。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼铝穷,長吁一口氣:“原來是場噩夢啊……” “哼钠怯!你這毒婦竟也來了?” 一聲冷哼從身側響起曙聂,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤晦炊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宁脊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體断国,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年榆苞,在試婚紗的時候發(fā)現(xiàn)自己被綠了稳衬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡语稠,死狀恐怖宋彼,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情仙畦,我是刑警寧澤输涕,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站慨畸,受9級特大地震影響莱坎,放射性物質發(fā)生泄漏。R本人自食惡果不足惜寸士,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一檐什、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧弱卡,春花似錦乃正、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凡人,卻和暖如春名党,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挠轴。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工传睹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岸晦。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓欧啤,卻偏偏與公主長得像睛藻,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子堂油,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容

  • 1修档、基本應用 Java泛型可以用在類碧绞、接口和方法上府框。基本使用請參考《on Java 8》. 2讥邻、類型擦除 ? ...
    流_心閱讀 314評論 0 0
  • 原文:https://blog.csdn.net/s10461/article/details/53941091 ...
    周蛋蛋閱讀 3,954評論 0 0
  • 1. 概述 泛型在java中有很重要的地位迫靖,在面向對象編程及各種設計模式中有非常廣泛的應用。 什么是泛型兴使?為什么要...
    Ricky_Zeng閱讀 452評論 1 6
  • 殷切期待的一場雪,悄悄下在我安睡的夢中励幼,醒來帶給我無限的驚喜汰寓。 此刻,窗外飄著雪苹粟,細細的有滑,輕輕的,柔柔的嵌削。 伸手抓...
    小雨飄飄閱讀 700評論 0 2
  • 坐在植物園的長凳上毛好,寫下這三天中考的心情。 中考的前一晚苛秕,孩子和我都是輾轉反側難以入睡肌访,我的心情...
    浪味仙lwx閱讀 68評論 0 0