天天看點

java 泛型_深入分析java中的泛型機制

想要學好java,泛型機制是一個必須要掌握的知識點,無奈很多書上寫的不是很啰嗦,就是概念太多難以了解,是以參考了很多篇文章,對其進行整理了一下,希望對你有所幫助。

一、認識泛型

1、為什麼要引入泛型?

泛型其實是在jdk1.5中才添加的。在jdk1.5之前我們要建立一個容器對象,是這樣往裡面添加内容的。

java 泛型_深入分析java中的泛型機制

也就是說我們建立了一個容器之後,我們可以往裡面添加任何東西,這時候就麻煩了,如果我們隻想儲存字元串,但是一不小心存了一個int類型的值,在輸出的時候肯定會報錯誤的。那怎麼辦呢?于是乎,在jdk1.5添加了泛型機制,去規範我們輸入的值。

java 泛型_深入分析java中的泛型機制

這時候我們的list就隻能儲存String類型的值了,如果我們儲存了int類型的值,那麼就會在編譯期報錯(一般情況下在ide寫代碼的時候,就會自動編譯)。

2、泛型概念

有了上面這個例子,我們再來了解一下泛型的概念:

泛型實作了參數化類型的概念,使得代碼可以應用于多種類型。

那什麼是參數化類型呢?也就是說把我們要操作的資料類型儲存為一個參數。比如下面這樣的

java 泛型_深入分析java中的泛型機制

我們把要操作的資料類型變成了一個“E”。這個E就是一個類型參數,我們可以指定E是具體String類型,也可以指定一個通配符,表示可以操作一類資料類型。

3、使用泛型的優點

在java中,官方強烈推薦我們使用泛型。就是因為他有很多優點。

(1)類型安全:我們在使用泛型之後,可以指定輸入的類型,比如隻能輸入String類型的值,輸入其他的就會報錯,這在代碼編寫時,為我們提供了極大的友善。

(2)消除強制類型轉換:也就是說我們不需要進行類型轉化,直接存儲、直接輸出。

(3)隻在編譯器有效:也就是說在運作時泛型是無效的。這避免了jvm花費時間在運作時做額外的操作。

對于第三點,我們這裡去驗證一下(這裡使用到了最基本的反射方法):

java 泛型_深入分析java中的泛型機制

在第三點其實已經給出答案了,輸出肯定是true。因為泛型隻在編譯器有效,在運作時期無效,也就變成了一樣的。就好比,在編譯時期一個是羊,一個是披着狼皮的羊,在外表看着不一樣。在運作時期,把狼皮脫掉了。就全暴露了,就都是羊了。

目前為止,我們已經把泛型的産生的原因(這隻是原因之一),泛型的概念以及泛型的優點說出來了,下面我們就來看看,泛型機制在java中是如何使用的。

二、泛型的使用

泛型的使用主要是在三個方面,泛型類、泛型接口、泛型方法。我們一個一個去看。

1、泛型類

泛型類的使用也是非常簡單的,和普通類的差別就是類名後有類型參數清單,既然是類型參數清單,也就是說可以有多個類型參數,比如。我們直接建立一個泛型類看看吧。

java 泛型_深入分析java中的泛型機制

我們會發現,其實泛型類和普通類的差別也就是有了一個參數類型清單:Generic。這裡的我們還可以添加任意多個。他就像String,Integer等等類型一樣。名字是我們取的。使用的時候,也是和String、Integer這些一樣。

下面我們就使用一下這個泛型類

java 泛型_深入分析java中的泛型機制

在使用這個泛型類的時候,有幾個地方需要我們去注意:

(1)執行個體化泛型類時,必須指定E和T的具體類型,比如這裡指定的是Integer和String

(2)指定的具體類型必須是類,不能是int,float等這些基礎類型

(3)不能對泛型類使用instanceof。為什麼呢?這是因為泛型類隻在編譯期有效,在運作時期不區分是什麼類型,也就是在上面說的,穿着狼皮的羊脫掉狼皮之後,兩隻羊就都一樣了。比如下面的代碼是不合法的。

java 泛型_深入分析java中的泛型機制

2、泛型接口

泛型接口其實和泛型類一樣,和普通接口的差別也是後面添加了類型參數清單。我們先建立一個泛型接口來看看。

java 泛型_深入分析java中的泛型機制

注意:在泛型接口裡面我們隻是定義了一個普通的方法,可不是泛型方法,然後我們就可以使用一般的接口那樣使用泛型接口了。

java 泛型_深入分析java中的泛型機制

在使用泛型接口時候和使用泛型類一樣同樣有幾個點需要我們知道:

(1)繼承泛型接口的時候就需要指定具體是什麼類型

(2)泛型中的方法也需要對相應的泛型參數賦予具體的類型。

3、泛型方法

泛型方法是什麼意思呢?也就是我們輸入參數的時候,輸入的是泛型參數,而不是具體的參數。我們在調用這個泛型方法的時候,需要對泛型參數執行個體化。我們還是直接看例子:

java 泛型_深入分析java中的泛型機制

這裡最重要的就是public後面的,隻有有了這個東西才稱得上泛型方法。當然這裡的也是一個泛型化清單。可以是。我們給出幾個普通方法,對比一下差別所在:

java 泛型_深入分析java中的泛型機制

現在我們知道差別了吧,也就是說泛型方法的标志就是,權限修飾符後面的。我們看一下如何去使用。

java 泛型_深入分析java中的泛型機制

我們可以像普通方法那樣去使用即可。

注意:在靜态方法中使用泛型參數的時候,需要我們把靜态方法定義為泛型方法

java 泛型_深入分析java中的泛型機制

4、泛型通配符

其實泛型通配符嚴格的劃分是屬于泛型類一部分的,為什麼要用到泛型通配符呢?因為有時候我們希望傳入的類型在一個指定的範圍内。舉個例子,之前我們傳入的類型必須指定為Integer類型的,但是後來業務變了,Integer的父類Number類也可以傳入。這時候就需要用到泛型通配符了。

泛型中有三種通配符形式:

(1)> 無限制通配符:表示我們可以傳入任意類型的參數 (2) extends E> 表示類型的上界是E,隻能是E或者是E的子孫類。 (3) super E> 聲明了類型的下界E,隻能是E或者是E的父類。

我們使用代碼舉個例子相信你就會明白了。

java 泛型_深入分析java中的泛型機制

5、類型擦除

我們在文章一開始就曾經說過,泛型隻在編譯期有效,在運作期虛拟機是分辨不出來的,而且我們還用反射機制來驗證了一下,發現在運作期兩個ArrayList确實是一樣的。那麼問題來了,從編譯期能夠識别泛型,再到運作期不能識别泛型肯定需要一個過程,在這個過程中編譯器肯定要對泛型進行一個處理,才能到運作期。這個處理就是類型擦除。

也就是說,在編譯時期java編譯器就完成了類型擦除。我們可以先看下面一種情況:

java 泛型_深入分析java中的泛型機制

上面我們定義了這兩個代碼會出現這樣的問題,這是因為java編譯器在編譯時期就進行了類型擦除,擦出了之後發現兩個方法的方法名、參數清單一樣。于是出現了兩個一樣的方法,報了這個錯誤。

上面出現的這種情況對我們來說真的是太麻煩了,如何解決這個問題呢?java又為我們提供了一個機制:邊界,來解決這個問題。什麼意思呢?之前我們的類型擦除,都是直接擦除到Object,現在有了邊界之後,我們隻擦出到一定的界限就不擦出了。我們再來看下面的使用了邊界之後的好處:

java 泛型_深入分析java中的泛型機制

現在應該能看明白了,我們限定了類型擦除的邊界之後,就不會出現這種錯誤了。編譯器會把類型參數替換為第一個邊界。如果你還不明白,就動手操作一遍。

三、泛型總結

如果我們之前了解過java中的文法糖的知識話,我們應該知道其是泛型就是一個文法糖,文法糖就是一個友善程式員的功能,對語言沒有任何影響。真正想要掌握泛型機制的話,還需要自己動手對每一塊内容自己寫一遍。OK,泛型就先到這裡。