Java泛型

導(dǎo)語

這里是導(dǎo)語脸甘。對(duì)恳啥,沒錯(cuò),這里就是導(dǎo)語丹诀,沒有前幾次的代碼情書钝的。喜歡情書的關(guān)注我,去看我之前的Java文章吧铆遭。

主要內(nèi)容

  • 泛型技術(shù)的產(chǎn)生背景
  • 泛型操作的實(shí)現(xiàn)
  • 通配符的使用
  • 泛型接口
  • 泛型方法

具體內(nèi)容

泛型的引出

現(xiàn)在要求定義一個(gè)表示坐標(biāo)的操作類(Point)硝桩,在這個(gè)類里面要求保存有以下幾種坐標(biāo):

  • 保存數(shù)字:x = 10、y = 20枚荣。
  • 保存小數(shù):x = 10.2碗脊、y = 20.3。
  • 保存字符串:x = 東經(jīng)20度橄妆、y = 北緯15度衙伶。

現(xiàn)在這個(gè)Point類設(shè)計(jì)的關(guān)鍵就在于x與y這兩個(gè)變量的類型設(shè)計(jì)上祈坠。必須有一種類型可以保存這三類數(shù)據(jù)。首先想到的一定是Object類型:

  • int:int自動(dòng)裝箱為Integer矢劲,Integer向上轉(zhuǎn)型為Object颁虐。
  • double:double自動(dòng)裝箱為Double,Double向上轉(zhuǎn)型為Object卧须。
  • String:直接向上轉(zhuǎn)型為Object。

范例:初期設(shè)計(jì)如下

public class Point {
    private Objects x;
    private Objects y;

    public Objects getX() {
        return x;
    }

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

    public Objects getY() {
        return y;
    }

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

下面重復(fù)的演示三個(gè)程序儒陨,分別使用各個(gè)不同的數(shù)據(jù)類型花嘶。
在Point類里面保存整型數(shù)據(jù)

public class TestDemo {
    public static void main(String args[]) {
        // 第一步:設(shè)置數(shù)據(jù)
        Point p = new Point();
        p.setX(10);
        p.setY(20);
        // 第二步:取出數(shù)據(jù)
        int x = (Integer) p.getX();
        int y = (Integer) p.getY();
        System.out.println("x坐標(biāo):" + x + ",y坐標(biāo):" + y + "蹦漠。");
    }
}

輸出結(jié)果

x坐標(biāo):10椭员,y坐標(biāo):20。

在Point類里面保存小數(shù)

public class TestDemo {
    public static void main(String args[]) {
        // 第一步:設(shè)置數(shù)據(jù)
        Point p = new Point();
        p.setX(10.2);
        p.setY(20.3);
        // 第二步:取出數(shù)據(jù)
        double x = (Double) p.getX();
        double y = (Double) p.getY();
        System.out.println("x坐標(biāo):" + x + "笛园,y坐標(biāo):" + y + "隘击。");
    }
}

輸出結(jié)果

x坐標(biāo):10.2,y坐標(biāo):20.3研铆。

在Point類里面保存字符串

public class TestDemo {
    public static void main(String args[]) {
        // 第一步:設(shè)置數(shù)據(jù)
        Point p = new Point();
        p.setX("東經(jīng)20度");
        p.setY("北緯15度");
        // 第二步:取出數(shù)據(jù)
        String x = (String) p.getX();
        String y = (String) p.getY();
        System.out.println("x坐標(biāo):" + x + "埋同,y坐標(biāo):" + y + "。");
    }
}

輸出結(jié)果

x坐標(biāo):東經(jīng)20度棵红,y坐標(biāo):北緯15度凶赁。

此時(shí)的代碼已經(jīng)利用了Object數(shù)據(jù)類型解決了一切的開發(fā)問題,可是解決的關(guān)系是靠的是Object逆甜,于是失敗的關(guān)鍵也在于Object虱肄。

范例:觀察錯(cuò)誤的代碼

public class TestDemo {
    public static void main(String args[]) {
        // 第一步:設(shè)置數(shù)據(jù)
        Point p = new Point();
        p.setX("東經(jīng)20度");
        p.setY(10);  // 這里改為數(shù)字
        // 第二步:取出數(shù)據(jù)
        String x = (String) p.getX();
        String y = (String) p.getY();
        System.out.println("x坐標(biāo):" + x + ",y坐標(biāo):" + y + "交煞。");
    }
}

代碼不需要執(zhí)行就可以看到程序的問題咏窿,因?yàn)樵诖娣诺臅r(shí)候是int(Integer),而取的時(shí)候使用的是String素征,兩個(gè)沒有發(fā)生關(guān)系的對(duì)象之間要發(fā)生強(qiáng)制轉(zhuǎn)換集嵌,就一定會(huì)產(chǎn)生ClassCastException。

向上轉(zhuǎn)型的核心目的在于統(tǒng)一操作的參數(shù)上稚茅,而向下轉(zhuǎn)型的目的是操作子類的特殊功能纸淮,可是現(xiàn)在的問題發(fā)現(xiàn)向下轉(zhuǎn)換是一件非常不安全的操作,那么這一操作應(yīng)該在代碼運(yùn)行之前就已經(jīng)能夠自動(dòng)的排查出來這是最好的選擇亚享⊙士椋可是之前的技術(shù)做不到。

泛型操作的實(shí)現(xiàn)

從JDK1.5之后開始增加了泛型技術(shù)欺税,而泛型技術(shù)的核心意義在于:類的定義的時(shí)候侈沪,可以使用一個(gè)標(biāo)記揭璃,此標(biāo)記就表示類中屬性或方法參數(shù)的類型標(biāo)記,在使用的時(shí)候才動(dòng)態(tài)的設(shè)置類型亭罪。

范例:修改代碼

// 此時(shí)設(shè)置的T在Point類定義上只表示一個(gè)標(biāo)記瘦馍,在使用的時(shí)候需要為其設(shè)置具體的類型
public class Point<T> {  // Type = T,是一個(gè)類型应役,也可是任何標(biāo)記
    private T x;  // 此屬性的類型不知道情组,由Point類使用時(shí)動(dòng)態(tài)決定
    private T y;  // 此屬性的類型不知道,由Point類使用時(shí)動(dòng)態(tài)決定
    
    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類的時(shí)候才去設(shè)置標(biāo)記的內(nèi)容箩祥,也就是設(shè)置了類中的屬性的類型院崇。

使用String

public class TestDemo {
    public static void main(String args[]) {
        // 第一步:設(shè)置數(shù)據(jù)
        Point<String> p = new Point<String>();
        p.setX("東經(jīng)20度");
        p.setY("北緯15度");  // 如果使用p.setY(10);會(huì)報(bào)錯(cuò)
        // 第二步:取出數(shù)據(jù)
        String x = (String) p.getX();
        String y = (String) p.getY();
        System.out.println("x坐標(biāo):" + x + ",y坐標(biāo):" + y + "袍祖。");
    }
}

使用了泛型之后底瓣,所有類中屬性的類型都是動(dòng)態(tài)設(shè)置的,而所有使用泛型標(biāo)記的方法參數(shù)類型也都發(fā)生改變蕉陋,這樣就相當(dāng)于避免了向下轉(zhuǎn)型的問題捐凭,從而解決了類轉(zhuǎn)換的安全隱患。
但是需要特別說明的是凳鬓,如果想要使用泛型茁肠,那么它能夠采用的類型只能夠是類,即:不能是基本類型村视,只能夠是引用類型(可以使用Integer)官套。

對(duì)于泛型有兩點(diǎn)說明:

  • 如果在使用泛型類或者是接口的時(shí)候,沒有設(shè)置泛型的具體類型蚁孔,那么會(huì)出現(xiàn)編譯時(shí)的警告奶赔,同是為了保證程序不出錯(cuò),所有的泛型都將使用Object表示杠氢。
  • 從JDK1.7開始可以簡(jiǎn)化聲明泛型站刑。

在JDK1.7之后實(shí)例化的時(shí)候可以寫成:

Point<Integer> p = new Point<>();

通配符

為了更好的理解通配符的作用,下面先來觀察一段程序鼻百。

public class Message<T> {
    private T msg;

    public T getMsg() {
        return msg;
    }

    public void setMsg(T msg) {
        this.msg = msg;
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Message<String> m = new Message<String>();
        m.setMsg("Hello World !");
        fun(m);  // 引用傳遞
    }
    
    public static void fun(Message<String> temp) {
        System.out.println(temp.getMsg());
    }
}

輸出結(jié)果

Hello World !

以上代碼為Message類設(shè)置的是一個(gè)String型的泛型類型绞旅,但是如果換成其它類型就不能夠使用了。

先看一組錯(cuò)誤代碼

public static void fun(Message temp) {  // 不設(shè)置接收參數(shù)為泛型温艇,就是Object類型
    temp.setMsg("Hello");  // 如果傳過來的泛型不為String就會(huì)報(bào)錯(cuò)
    System.out.println(temp.getMsg());
}

正確的代碼如下

public static void fun(Message<?> temp) {  // 設(shè)置為通配符問號(hào)因悲,表示不能夠設(shè)置,只能夠取出
    System.out.println(temp.getMsg());
}

在“?”通配符基礎(chǔ)上還會(huì)有兩個(gè)子的通配符:

  • ? extends類:設(shè)置泛型上限勺爱,可以在聲明上和方法參數(shù)上使用晃琳。
    • ? extends Number:意味著可以設(shè)置Number或者Number的子類(Integer、Double...)。
  • ? super類:設(shè)置泛型下限卫旱,方法參數(shù)上使用人灼。
    • ? super String:意味著只能夠設(shè)置String或者是它的父類Object。

范例:設(shè)置泛型的上限

public class Message<T extends Number> {
    private T msg;

    public T getMsg() {
        return msg;
    }

    public void setMsg(T msg) {
        this.msg = msg;
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Message<Integer> m = new Message<Integer>();
        m.setMsg(100);
        fun(m);  // 引用傳遞
    }
    
    public static void fun(Message<? extends Number> temp) {
        System.out.println(temp.getMsg());
    }
}

如果設(shè)置了非Number或者其子類的話顾翼,那么將出現(xiàn)語法錯(cuò)誤投放。

范例:設(shè)置泛型下限

public class Message<T> {
    private T msg;

    public T getMsg() {
        return msg;
    }

    public void setMsg(T msg) {
        this.msg = msg;
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Message<String> m = new Message<String>();
        m.setMsg("Hello");
        fun(m);  // 引用傳遞
    }
    
    public static void fun(Message<? super String> temp) {
        System.out.println(temp.getMsg());
    }
}

泛型接口

在之前都是將泛型定義在了一個(gè)類里面,那么泛型也可以定義在接口上聲明适贸,稱為泛型接口灸芳。

范例:定義泛型接口

public interface Message<T> {
    public void print(T t);
}

定義子類有兩種形式。

形式一:在子類繼續(xù)設(shè)置泛型

// 子類也繼續(xù)使用泛型拜姿,并且父接口使用和子類同樣的泛型標(biāo)記
public class MessageImpl<T> implements Message<T> {
    public void print(T t) {
        System.out.println(t);
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Message<String> msg = new MessageImpl<String>();
        msg.print("Hello World !");
    }
}

形式二:在子類不設(shè)置泛型耗绿,而為父接口明確的定義一個(gè)泛型類型

// 子類也繼續(xù)使用泛型,并且父接口使用和子類同樣的泛型標(biāo)記
public class MessageImpl implements Message<String> {
    public void print(String t) {
        System.out.println(t);
    }
}

public class TestDemo {
    public static void main(String args[]) {
        Message<String> msg = new MessageImpl();
        msg.print("Hello World !");
    }
}

泛型方法

泛型方法不一定要定義在支持泛型的類里面砾隅。

范例:泛型方法定義

public class TestDemo {
    public static void main(String args[]) {
        String str = fun("Hello");
        Sysout.out.println(str.length);
    }
    
    // T的類型由傳入的參數(shù)類型決定
    public static <T> T fun(T t) {
        return t;
    }
}

總結(jié)

  • 泛型解決的是向下轉(zhuǎn)型所帶來的安全隱患,其核心的組成就是在聲明類或接口的時(shí)候不設(shè)置參數(shù)或?qū)傩缘念愋汀?/li>
  • “?”可以接收任意的泛型類型债蜜,只能夠取出晴埂,但是不能夠修改。

更多內(nèi)容戳這里(整理好的各種文集)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寻定,一起剝皮案震驚了整個(gè)濱河市儒洛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狼速,老刑警劉巖琅锻,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異向胡,居然都是意外死亡恼蓬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門僵芹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來处硬,“玉大人,你說我怎么就攤上這事拇派『稍” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵件豌,是天一觀的道長(zhǎng)疮方。 經(jīng)常有香客問我,道長(zhǎng)茧彤,這世上最難降的妖魔是什么骡显? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上蟆盐,老公的妹妹穿的比我還像新娘承边。我一直安慰自己,他們只是感情好石挂,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布博助。 她就那樣靜靜地躺著,像睡著了一般痹愚。 火紅的嫁衣襯著肌膚如雪富岳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天拯腮,我揣著相機(jī)與錄音窖式,去河邊找鬼。 笑死动壤,一個(gè)胖子當(dāng)著我的面吹牛萝喘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播琼懊,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼阁簸,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了哼丈?” 一聲冷哼從身側(cè)響起启妹,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎醉旦,沒想到半個(gè)月后饶米,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡车胡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年檬输,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匈棘。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡褪猛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出羹饰,到底是詐尸還是另有隱情伊滋,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布队秩,位于F島的核電站笑旺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏馍资。R本人自食惡果不足惜筒主,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乌妙,春花似錦使兔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泽艘,卻和暖如春欲险,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匹涮。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工天试, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人然低。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓喜每,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親雳攘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灼卢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • 我們知道,使用變量之前要定義来农,定義一個(gè)變量時(shí)必須要指明它的數(shù)據(jù)類型,什么樣的數(shù)據(jù)類型賦給什么樣的值崇堰。 假如我們現(xiàn)在...
    e347afbb188e閱讀 297評(píng)論 0 0
  • 我們知道沃于,使用變量之前要定義,定義一個(gè)變量時(shí)必須要指明它的數(shù)據(jù)類型海诲,什么樣的數(shù)據(jù)類型賦給什么樣的值繁莹。 假如我們現(xiàn)在...
    今晚打肉山閱讀 986評(píng)論 0 1
  • Java泛型總結(jié)# 泛型是什么## 從本質(zhì)上講,泛型就是參數(shù)化類型特幔。泛型十分重要咨演,使用該特性可以創(chuàng)建類、接口以及方...
    kylinxiang閱讀 921評(píng)論 0 1
  • 開發(fā)人員在使用泛型的時(shí)候蚯斯,很容易根據(jù)自己的直覺而犯一些錯(cuò)誤薄风。比如一個(gè)方法如果接收List作為形式參數(shù),那么如果嘗試...
    時(shí)待吾閱讀 1,057評(píng)論 0 3
  • 漸漸的拍嵌,你好像真的忘了自己的樣子遭赂。 你忘記了堅(jiān)持忘記了堅(jiān)持的樣子,你忘記了生活的酸甜苦辣横辆,你忘記了你自己和身邊的一...
    Black兔閱讀 167評(píng)論 0 2