淺談Java泛型

使用泛型的目的

當我們第一次接觸泛型時狡孔,第一個問題肯定會是撩匕,為什么要使用泛型害捕?最直接的答案是為了避免轉型绿淋,使得編譯器能夠在編譯期就發(fā)現(xiàn)轉型錯誤而不用等到運行時。

比如說尝盼,當我們聲明了一個泛型為Integer的列表吞滞,那么該列表的元素就只能是Integer,當我們往里面放非Integer的元素時东涡,編譯器就能夠發(fā)現(xiàn)冯吓,

List<Integer> intList = new ArrayList<>();
intList.add("abc"); //編譯期錯誤,不能存放String對象到intList中

同理疮跑,當我們從列表中獲取元素時组贺,我們也不再需要使用強制轉型將列表中的元素轉型成Integer

Integer i = intList.get(0);
Integer i = (Integer)intList.get(0);//不再需要進行強制轉型

然而,泛型卻有一些容易令人困惑的地方祖娘,第一個就是關于泛型的帶有通配符的上下界失尖。

帶有通配符的上下界

Java泛型現(xiàn)在支持兩種帶有通配符的上下界的表達方式啊奄,

  1. ? extends T - 這里的?表示類型T的任意子類型,包括類型T本身掀潮。
  2. ? super T - 這里的?表示類型T的任意父類型菇夸,包括類型T本身。

這兩者的含義都很容易理解和區(qū)分仪吧,難點在于我們什么時候該用 ? extends T, 什么時候改用? super T.

比如說庄新,由于? extends T表示類型T和它的任意子類型,那么我們可以說List<? extends Number> 實例可以添加任意為Number子類的元素嗎薯鼠?

    List<? extends Number> intList = new ArrayList<>();
    intList.add(1); //complier error
    intList.add(3.14); //compiler error

從上述的代碼來看择诈,它并不像我們所理解的那樣工作。那么出皇,我們在什么時候需要使用? extends T和? super T呢羞芍? 這里有一條簡單的規(guī)則 (Get and Put Rule):

當你需要從一個數(shù)據結構中獲取數(shù)據時(get), 那么就使用? extends T. 如果你需要存儲數(shù)據(put)到一個數(shù)據結構時,那么就使用? super T. 如果你又想存儲數(shù)據郊艘,又想獲取數(shù)據荷科,那么就不要使用通配符? . 即直接使用具體泛型T。

所以纱注,根據Get&Put規(guī)則畏浆,在上面的例子中,我們是需要往數(shù)據結構里面存儲數(shù)據的奈附,所以需要使用? super T. 修改上面的例子全度,你可以發(fā)現(xiàn)程序可以工作了煮剧。

    List<? super Number> intList = new ArrayList<>();
    intList.add(1); //it works
    intList.add(3.14); //it works

第二個比較讓人困惑的是關于泛型的類型系統(tǒng)斥滤。

泛型的類型系統(tǒng)

泛型的類型系統(tǒng)中最令人困惑的地方就是,Integer類是Number類的子類勉盅,而List<Integer>卻不是List<Number>的子類佑颇。也就是說,你不能將List<Integer>實例對象直接賦值給List<Number>實例對象草娜。 List<Integer>不是List<Number>子類的原因我們只能通過反證法推理出來:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<Number> nums = ints; // compile-time error
nums.add(3.14);
assert ints.toString().equals("[1, 2, 3.14]"); // uh oh!

從上面的例子中挑胸,假設List<Integer>是List<Number>的子類,那么List<Integer>實例對象ints就可以賦值給List<Number>實例對象nums宰闰。接著我們可以往nums實例添加非Integer對象茬贵,如float對象3.14。但是這個是不允許的移袍,因為nums實例實際上是List<Integer>實例對象解藻,它不能接受任何非Integer對象。由此可證葡盗,List<Integer>并不是List<Number>的子類螟左。但是,List<Integer>是Collection<Integer>的子類。

所以對于泛型的類型系統(tǒng)來講胶背,它應當遵循以下一些規(guī)則巷嚣,摘抄自Java深度歷險-Java泛型

引入泛型之后的類型系統(tǒng)增加了兩個維度:一個是類型參數(shù)自身的繼承體系結構,另外一個是泛型類或接口自身的繼承體系結構钳吟。第一個指的是對于List<String>和List<Object>這樣的情況廷粒,類型參數(shù)String是繼承自Object的。而第二種指的是List接口繼承自Collection接口红且。對于這個類型系統(tǒng)评雌,有如下的一些規(guī)則:

  1. 相同類型參數(shù)的泛型類的關系取決于泛型類自身的繼承體系結構。即List<String>是Collection<String> 的子類型直焙,List<String>可以替換Collection<String>景东。這種情況也適用于帶有上下界的類型聲明。
  1. 當泛型類的類型聲明中使用了通配符的時候奔誓,其子類型可以在兩個維度上分別展開斤吐。如對Collection<? extends Number>來說,其子類型可以在Collection這個維度上展開厨喂,即List<? extends Number>和Set<? extends Number>等和措;也可以在Number這個層次上展開,即Collection<Double>和 Collection<Integer>等蜕煌。如此循環(huán)下去派阱,ArrayList<Long>和 HashSet<Double>等也都算是Collection<? extends Number>的子類型。
  2. 如果泛型類中包含多個類型參數(shù)斜纪,則對于每個類型參數(shù)分別應用上面的規(guī)則贫母。

最后我們來看看在Java泛型參數(shù)是如何進行推斷的。

泛型參數(shù)的自動推斷

有兩種方式可以指定泛型參數(shù)的類型盒刚,一種是顯示的指定腺劣,比如對于下面的方法,

public class GenericUtil {
    public static <T> List<T> asList(T... array) {
        List<T> tlist = new ArrayList<>();
        for (T t : array) {
            tlist.add(t);
        }
        return tlist;
    }
}

我們可以顯示的指定泛型參數(shù)的類型因块,

System.out.println(GenericUtil.<String>asList("a","b","c"));

除了顯示的指定之外橘原,我們還可以隱式的指定, 實際上就是讓編譯器自己去推斷涡上。

System.out.println(GenericUtil.asList("a","b","c"));

編譯器可以通過asList方法接收的參數(shù)類型來推斷出T實際上就是String趾断。 如果從方法接收的參數(shù)類型推斷不出來的話,那么編譯器還會從方法賦值的目標參數(shù)來推斷吩愧,比如說下面的newObject方法芋酌,

public class GenericUtil {

    public static <T> T newObject(String className) throws Exception {
        Class clz = Class.forName(className);
        return (T) clz.newInstance();
    }
}

從方法接收參數(shù)上編譯器并不能推斷出T是什么類型。這個時候可以從方法賦值的目標參數(shù)進行推斷耻警,

  Person p = GenericUtil.newObject("com.kevin.hibernate01.Person");

從方法賦值的目標參數(shù)p可以得知隔嫡,newObject方法中定義的泛型參數(shù)T類型應該為Person類型甸怕。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腮恩,隨后出現(xiàn)的幾起案子梢杭,更是在濱河造成了極大的恐慌,老刑警劉巖秸滴,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件武契,死亡現(xiàn)場離奇詭異,居然都是意外死亡荡含,警方通過查閱死者的電腦和手機咒唆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來释液,“玉大人全释,你說我怎么就攤上這事∥笳” “怎么了浸船?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寝蹈。 經常有香客問我李命,道長,這世上最難降的妖魔是什么箫老? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任封字,我火速辦了婚禮,結果婚禮上耍鬓,老公的妹妹穿的比我還像新娘阔籽。我一直安慰自己,他們只是感情好界斜,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布仿耽。 她就那樣靜靜地躺著合冀,像睡著了一般各薇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上君躺,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天峭判,我揣著相機與錄音,去河邊找鬼棕叫。 笑死林螃,一個胖子當著我的面吹牛,可吹牛的內容都是我干的俺泣。 我是一名探鬼主播疗认,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼完残,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了横漏?” 一聲冷哼從身側響起谨设,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缎浇,沒想到半個月后扎拣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡素跺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年二蓝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片指厌。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡刊愚,死狀恐怖,靈堂內的尸體忽然破棺而出踩验,到底是詐尸還是另有隱情百拓,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布晰甚,位于F島的核電站衙传,受9級特大地震影響,放射性物質發(fā)生泄漏厕九。R本人自食惡果不足惜蓖捶,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扁远。 院中可真熱鬧俊鱼,春花似錦、人聲如沸畅买。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谷羞。三九已至帝火,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間湃缎,已是汗流浹背犀填。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嗓违,地道東北人九巡。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像蹂季,于是被迫代替她去往敵國和親冕广。 傳聞我的和親對象是個殘疾皇子疏日,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容

  • 1. 泛型概述 泛型為JDK1.5之后sun公司推出的新功能,泛型可以消除源代碼中的許多強制類型轉換撒汉,泛型對于數(shù)據...
    yuan_dongj閱讀 4,484評論 0 8
  • 第8章 泛型 通常情況的類和函數(shù)制恍,我們只需要使用具體的類型即可:要么是基本類型,要么是自定義的類神凑。但是在集合類的場...
    光劍書架上的書閱讀 2,143評論 6 10
  • 開發(fā)人員在使用泛型的時候净神,很容易根據自己的直覺而犯一些錯誤。比如一個方法如果接收List作為形式參數(shù)溉委,那么如果嘗試...
    時待吾閱讀 1,042評論 0 3
  • 我到底是愛你還是不愛你呢鹃唯?是因為愛你才在乎你還是因為你不在乎我我才更在乎你?如果你不愛我或者沒那么愛我最重要的是沒...
    水也云云閱讀 195評論 0 0
  • 風清揚瓣喊,雪舞狂坡慌,這路上,我在藻三! 每個人的路都有自己的走法洪橘,我們各行其道,各運常修棵帽,讓線譜交織熄求,讓生命喝彩。 從來到...
    人泥閱讀 127評論 0 1