java泛型那些事

泛型:
一種程序設(shè)計(jì)語(yǔ)言的新特性,于Java而言,在JDK 1.5開(kāi)始引入鸦采。泛型就是在設(shè)計(jì)程序的時(shí)候定義一些可變部分,在具體使用的時(shí)候再給可變部分指定具體的類型咕幻。使用泛型比使用Object變量再進(jìn)行強(qiáng)制類型轉(zhuǎn)換具有更好的安全性和可讀性渔伯。在Java中泛型主要體現(xiàn)在泛型類、泛型方法和泛型接口中肄程。

Java泛型中的標(biāo)記符含義:

E - Element (在集合中使用锣吼,因?yàn)榧现写娣诺氖窃?

T - Type(Java 類)

K - Key(鍵)

V - Value(值)

N - Number(數(shù)值類型)

? - 表示不確定的java類型

S蓝厌、U玄叠、V - 2nd、3rd拓提、4th types

泛型類

什么時(shí)候使用泛型類读恃?
只要類中操作的引用數(shù)據(jù)類型不確定,就可以定義泛型類代态。通過(guò)使用泛型類寺惫,可以省去強(qiáng)制類型轉(zhuǎn)換和類型轉(zhuǎn)化異常的麻煩。

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}
public class Container<k,V>{
        private k key;
        private V value;
        public Container(K k,V v){
                key=k;
                value=v;
        }
        public K getkey(){
                return key;
        }
        public V getValue(){
                  return value
        }
        public void setKey(){
                this.key=key;
        }
        public void setValue(){
              this.value=value;
        }
}

泛型方法

泛型方法也是為了提高代碼的重用性和程序安全性蹦疑。編程原則:盡量設(shè)計(jì)泛型方法解決問(wèn)題西雀,如果設(shè)計(jì)泛型方法可以取代泛型整個(gè)類,應(yīng)該采用泛型方法歉摧。
泛型方法的格式:類型變量放在修飾符后面和返回類型前面艇肴, 如:public static <E> E getMax(T... in)

public class GenericFunc {  
  
    public static void main(String[] args) {  
        print("hahaha");  
        print(200);  
    }  
      
    public static <T> void print(T t){  
        System.out.println(t.toString());  
    }  
  
}  

泛型接口

將泛型原理用于接口實(shí)現(xiàn)中,就是泛型接口叁温。
泛型接口的格式:泛型接口格式類似于泛型類的格式豆挽,接口中的方法的格式類似于泛型方法的格式。

public interface MyInteface<T> {  
    public T read(T t);  
}
public class Generic2 implements MyInterface<String>{  
  
    public static void main(String[] args) {  
        Generic2 g = new Generic2();  
        System.out.println(g.read("hahaha"));  
    }  
  
    @Override  
    public String read(String str) {  
        return str;  
    }  
  
}  

通配符

當(dāng)操作的不同容器中的類型都不確定的時(shí)候券盅,而且使用的元素都是從Object類中繼承的方法帮哈,這時(shí)泛型就用通配符“?”來(lái)表示。泛型的通配符:“?” 相當(dāng)于 “贬养? extends Object”

  • <? extends T>:是指 “上界通配符(Upper Bounds Wildcards)
  • <? super T>:是指 “下界通配符(Lower Bounds Wildcards)”
  1. 為什么要用通配符和邊界惯疙?
    比如我們有Fruit類廉丽,和它的派生類Apple類政冻。
class Fruit {}
class Apple extends Fruit {}

然后有一個(gè)最簡(jiǎn)單的容器:Plate類

public class Plate<T> {
    private T item;
    public Plate(T item) {
        super();
        this.item = item;
    }
    public T getItem() {
        return item;
    }
    public void setItem(T item) {
        this.item = item;
    }
}

現(xiàn)在我定義一個(gè)“水果盤子”的止,邏輯上水果盤子當(dāng)然可以裝蘋果座云。

Plate<Fruit> p=new Plate<Apple>(new Apple());

但實(shí)際上Java編譯器不允許這個(gè)操作发侵。會(huì)報(bào)錯(cuò)氧腰,“裝蘋果的盤子”無(wú)法轉(zhuǎn)換成“裝水果的盤子”枫浙。

Type mismatch: cannot convert from Plate<Apple> to Plate<Fruit>

實(shí)際上,編譯器腦袋里認(rèn)定的邏輯是這樣的:

  • 蘋果 IS-A 水果
  • 裝蘋果的盤子 NOT-IS-A 裝水果的盤子

所以就算容器里面裝的東西有繼承關(guān)系古拴,但是容器是沒(méi)有繼承關(guān)系的箩帚,所以不可以把Plate<Apple>的引用賦值給Plate<Fruit>
為了讓泛型使用起來(lái)更靈活,sun的大牛就想出了<? extends T>和<? super T>的辦法黄痪,讓水果盤子和蘋果盤子發(fā)生關(guān)系

“上界通配符(Upper Bounds Wildcards)”:

Plate<紧帕? extends Fruit>

翻譯成人話就是:一個(gè)能放水果以及一切是水果派生類的盤子。再直白點(diǎn)就是:啥水果都能放的盤子桅打。這和我們?nèi)祟惖倪壿嬀捅容^接近了是嗜。Plate<? extends Fruit>和Plate<Apple>最大的區(qū)別就是:Plate<挺尾? extends Fruit>是Plate<Fruit>以及Plate<Apple>的基類鹅搪。直接的好處就是,我們可以用“蘋果盤子”給“水果盤子”賦值了遭铺。

Plate<? extends Fruit> p=new Plate<Apple>(new Apple());

如果把Fruit和Apple的例子再擴(kuò)展一下丽柿,食物分成水果和肉類,水果有蘋果和香蕉掂僵,肉類有豬肉和牛肉航厚,蘋果還有兩種青蘋果和紅蘋果顷歌。

//Lev 1
class Food{}

//Lev 2
class Fruit extends Food{}
class Meat extends Food{}

//Lev 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}

//Lev 4
class RedApple extends Apple{}
class GreenApple extends Apple{}

在這個(gè)體系中锰蓬,上界通配符 “Plate<? extends Fruit>” 覆蓋下圖中藍(lán)色的區(qū)域眯漩。


cdec0a066693684036d4bcaab4fdc1e3_hd.jpg

“下界通配符(Lower Bounds Wildcards)”:

Plate<芹扭? super Fruit>

表達(dá)的就是相反的概念:一個(gè)能放水果以及一切是水果基類的盤子。Plate<赦抖? super Fruit>是Plate<Fruit>的基類舱卡,但不是Plate<Apple>的基類。對(duì)應(yīng)剛才那個(gè)例子队萤,Plate<轮锥? super Fruit>覆蓋下圖中紅色的區(qū)域。


0800ab14b2177e31ee3b9f6d477918fa_r.jpg
上下界通配符的副作用

邊界讓Java不同泛型之間的轉(zhuǎn)換更容易了要尔。但不要忘記舍杜,這樣的轉(zhuǎn)換也有一定的副作用新娜。那就是容器的部分功能可能失效。
還是以剛才的Plate為例既绩。我們可以對(duì)盤子做兩件事概龄,往盤子里set( )新東西,以及從盤子里get( )東西饲握。

class Plate<T>{
    private T item;
    public Plate(T t){item=t;}
    public void set(T t){item=t;}
    public T get(){return item;}
}
上界<? extends T>不能往里存私杜,只能往外取

<? extends Fruit>會(huì)使往盤子里放東西的set( )方法失效。但取東西get( )方法還有效救欧。比如下面例子里兩個(gè)set()方法衰粹,插入Apple和Fruit都報(bào)錯(cuò)。

Plate<? extends Fruit> p=new Plate<Apple>(new Apple());

//不能存入任何元素
p.set(new Fruit());    //Error
p.set(new Apple());    //Error

//讀取出來(lái)的東西只能存放在Fruit或它的基類里颜矿。
Fruit newFruit1=p.get();
Object newFruit2=p.get();
Apple newFruit3=p.get();    //Error

原因是編譯器只知道容器內(nèi)是Fruit或者它的派生類寄猩,但具體是什么類型不知道∑锝可能是Fruit田篇?可能是Apple?也可能是Banana箍铭,RedApple泊柬,GreenApple?編譯器在看到后面用Plate<Apple>賦值以后诈火,盤子里沒(méi)有被標(biāo)上有“蘋果”兽赁。而是標(biāo)上一個(gè)占位符:CAP#1,來(lái)表示捕獲一個(gè)Fruit或Fruit的子類冷守,具體是什么類不知道刀崖,代號(hào)CAP#1。然后無(wú)論是想往里插入Apple或者M(jìn)eat或者Fruit編譯器都不知道能不能和這個(gè)CAP#1匹配拍摇,所以就都不允許亮钦。

所以通配符<?>和類型參數(shù)<T>的區(qū)別就在于,對(duì)編譯器來(lái)說(shuō)所有的T都代表同一種類型充活。比如下面這個(gè)泛型方法里蜂莉,三個(gè)T都指代同一個(gè)類型,要么都是String混卵,要么都是Integer映穗。
public <T> List<T> fill(T... t);
但通配符<?>沒(méi)有這種約束,Plate<?>單純的就表示:盤子里放了一個(gè)東西幕随,是什么我不知道蚁滋。
所以錯(cuò)誤就在這里,Plate<? extends Fruit>里什么都放不進(jìn)去辕录。

下界<? super T>不影響往里存澄阳,但往外取只能放在Object對(duì)象里

使用下界<? super Fruit>會(huì)使從盤子里取東西的get( )方法部分失效,只能存放到Object對(duì)象里踏拜。set( )方法正常碎赢。

Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());

//存入元素正常
p.set(new Fruit());
p.set(new Apple());

//讀取出來(lái)的東西只能存放在Object類里。
Apple newFruit3=p.get();    //Error
Fruit newFruit1=p.get();    //Error
Object newFruit2=p.get();

因?yàn)橄陆缫?guī)定了元素的最小粒度的下限速梗,實(shí)際上是放松了容器元素的類型控制肮塞。既然元素是Fruit的基類,那往里存粒度比Fruit小的都可以姻锁。但往外讀取元素就費(fèi)勁了枕赵,只有所有類的基類Object對(duì)象才能裝下。但這樣的話位隶,元素的類型信息就全部丟失拷窜。

PECS原則

最后看一下什么是PECS(Producer Extends Consumer Super)原則,已經(jīng)很好理解了:

  • 頻繁往外讀取內(nèi)容的涧黄,適合用上界Extends篮昧。
  • 經(jīng)常往里插入的,適合用下界Super笋妥。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末懊昨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子春宣,更是在濱河造成了極大的恐慌酵颁,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件月帝,死亡現(xiàn)場(chǎng)離奇詭異躏惋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)嚷辅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門簿姨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人潦蝇,你說(shuō)我怎么就攤上這事款熬∩盍龋” “怎么了攘乒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)惋鹅。 經(jīng)常有香客問(wèn)我则酝,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任沽讹,我火速辦了婚禮般卑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爽雄。我一直安慰自己蝠检,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布挚瘟。 她就那樣靜靜地躺著叹谁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乘盖。 梳的紋絲不亂的頭發(fā)上焰檩,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音订框,去河邊找鬼析苫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛穿扳,可吹牛的內(nèi)容都是我干的衩侥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼矛物,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顿乒!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起泽谨,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤璧榄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吧雹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體骨杂,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年雄卷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搓蚪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丁鹉,死狀恐怖妒潭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揣钦,我是刑警寧澤雳灾,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站冯凹,受9級(jí)特大地震影響谎亩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一匈庭、第九天 我趴在偏房一處隱蔽的房頂上張望夫凸。 院中可真熱鬧,春花似錦阱持、人聲如沸夭拌。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)啼止。三九已至,卻和暖如春兵罢,著一層夾襖步出監(jiān)牢的瞬間献烦,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工卖词, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留巩那,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓此蜈,卻偏偏與公主長(zhǎng)得像即横,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裆赵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

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

  • 泛型的類型安全性 有許多原因促成了泛型的出現(xiàn)东囚,而最引人注意的一個(gè)原因,就是為了創(chuàng)建容器類战授。 如果沒(méi)有泛型,如果我們...
    嘉偉咯閱讀 1,300評(píng)論 0 9
  • 值傳遞和引用傳遞 值傳遞是值的拷貝, 引用傳遞是引用的拷貝 String 類型是引用類型, new String ...
    我不是死胖子閱讀 279評(píng)論 0 0
  • 在泛型概述-基本概念當(dāng)中页藻,我們介紹了有關(guān)類型參數(shù)限定的概念,使用 extends 關(guān)鍵字植兰,給類型參數(shù)加以限定份帐,例如...
    realxz閱讀 674評(píng)論 0 1
  • 「天長(zhǎng)地久有時(shí)盡,此恨綿綿無(wú)絕期」楣导,《長(zhǎng)恨歌》為什么要叫長(zhǎng)恨歌废境? 因?yàn)闂钣癍h(huán)說(shuō),她和她的三郎立誓要「在天愿做比翼鳥(niǎo)...
    知月閱讀 1,669評(píng)論 2 45
  • 生物的學(xué)科思想 ………是學(xué)科要求筒繁,學(xué)科自身思想噩凹。 物質(zhì)構(gòu)成觀 結(jié)構(gòu)決定功能觀 生命結(jié)構(gòu)的系統(tǒng)化觀,包含結(jié)構(gòu)毡咏,與環(huán)境...
    昭昭快樂(lè)風(fēng)閱讀 1,432評(píng)論 1 2