看完這篇10000字文章,我就不信你還吃不透Java的泛型!

2020-11-12 18:02:37

前言

在學習Java基礎的過程中,泛型絕對算得上是一個比較難理解的知識點,尤其對於初學者而言,而且就算是已經有基礎的Java程式設計師,可能對泛型的理解也不是那麼透徹,屬於那種看了明白,時間長了就忘的那種,究其根本,還是對泛型不夠理解。

大部分人對泛型的認識:「基礎知識,但是比較模糊」

為啥要有泛型

泛型這個概念是在Java1.5提出來的,之前是沒有的,那為什麼之前沒有,現在要提出來這個概念呢?那你就得想啊:

1、要麼是之前的技術太垃圾了,得升級換代下
2、要麼是技術發展,搞出來的新玩意,讓Java更好用
3、要麼就是填坑,之前的有部分,搞個出來填坑
4、……

那你看,這個泛型,有可能是啥,實際上它就是填坑的,說這個之前,咱們先來看一段程式碼:

這段程式碼沒有看不懂的吧,就是簡單的一個List集合,這裡可能需要你額外注意的就是我這裡定義的List不是這樣的哦:

List<String> stringList = new ArrayList<String>();

不知道你看出來區別了嗎?這個應該是大部分我們用的時候會定義的那種,熟悉泛型的可能知道這是怎麼回事,但是不熟悉泛型的就記住這麼用就ok了。

繼續看上面我舉的那個例子,這裡我想說明的就是這裡定義的List集合是什麼都能裝的,集合一般啥用,不就是往裡面存放資料的嘛,把集合看做一個房子,裡面可以進去老人和小孩,當然各種貓啊狗啊都可以進去,來,上圖:

咋樣,我的繪畫功底還是可以吧,List就好比一個房子,裡面是可以進人,當然也可以進動物,比如各種各樣的小貓小狗啊,當然了,List這裡可進入的就是資料了,比如字串啊,整型啊等等。

這個時候就會產生一個問題,啥問題嘞,你看啊,這座房子裡面可以進入各種各樣的物種,就是各種型別的啊,如果這個房子會說話的話,它就說了「啥玩意啊,你們啥都往我這來,啥品種都行,也太亂了吧,都記不清楚你們誰是誰了,算了我也不記住你們誰是啥型別了,進來的統一當做‘活物’吧」

就是說啊,對於房子來說,不管你進來的是個啥,我統一把你們當做「活物」,在我這裡你們就是一個型別的,同樣的,放到List集合這裡來說的話,也是這樣的,就是你字串啊,整型啊都可以放入List集合中,那麼List也說了,我不記住你們到底哪個是字串哪個是整型,在我這裡統一把你們看做是Object類,至於為啥是Object類,這是因為Object是一切類的父類別啊,所以沒有比它更合適啦,不管你是字串還是整型,你們一定都是Object型別的,這點容易理解吧。

這樣看似挺完美的,但是也有潛在的問題啊,什麼問題嘞,你看啊,我們還拿房子來說,對於房子來說,在房子裡面的都被看成是「活物」型別了,那麼如果我要從房子裡找一個小狗出來,那麼我找出來的型別都是「活物」,可是我要的是小狗啊,你給我來個「活物」,不符合我的要求啊,怎麼辦?大家都知道有個強制型別轉換吧,好吧,我就把「活物」給你強制轉換成小狗,這不就符合要求了,但是這樣問題就來了,假如你找出來的就是個小狗,你把它強制轉換成小狗,那倒也沒啥,但是你也有可能會找出來個人啊,這時候你要把一個人強制轉換成小狗,擱誰誰也不樂意啊,你說是不是。

同樣的,對於List集合也是這樣,你什麼都可以往裡面儲存,然後統一被看成是Object型別,這個時候如果我們從List集合中取值的話,那就要用到強制轉換了,需要把Object型別轉換成我們要的型別,比如我們想要字串,如果取出的本來就是字串,那轉成字串沒啥關係,但是如果取出來的是整型嘞,強制轉換成字串,那整型還不樂意嘞,於是乎,程式就要給你報錯了,來來,看看程式碼:

咋一看,貌似沒啥問題,編譯也沒報錯啥的,我們執行一下看看:

吆喝,執行報錯了,這個錯也簡單,就是不能將整型Integer轉換成String字串型別,就是型別不匹配啊,這裡要記住這個錯誤型別叫做:ClassCastException
看到這裡你也許就明白了,我定義一個List集合,本來只想往裡面存入字串嘞,但是不知怎麼滴,裡面混入了一個整型,因為List並不知道進來的都是什麼型別,反正都看作Object型別,都可以進來,那麼我們取資料的時候就會發生ClassCastException錯誤。
那這不行啊,我給你List裡面存一個資料,我是希望你記住我傳入的是什麼型別的資料的,其他的就不允許再傳入了,這樣我取值的時候也不用強制型別轉換,也就不會發生ClassCastException了。

這個你能想到,那麼JDK官方更加能想到,於是乎,在Java1.5版本中就引入了泛型的概念,而引入泛型的很大一部分原因就是為了解決我們上述的問題,說白了就是我希望集合可以記得住我存進去的資料是什麼型別的,以此做一個篩選,不是同型別的就不允許在一塊存放,這樣也避免了ClassCastException錯誤的出現,因為都是同一型別,也就沒必要做強制型別轉換了。

所以,你現在知道了為啥要有泛型了吧,當然,泛型的引入大部分原因是為了彌補集合的一個缺點,但是泛型的應用是很廣的,不僅僅侷限於Java的集合。

泛型的定義和理解

以上還算詳細的和大家介紹了為啥要有泛型的出現,那麼泛型是如何定義的呢?

泛型就是引數化型別,也就是說把我們要操作的型別作為了一個引數,比如我們建立集合的時候,允許我們可以指定集合中元素的資料型別。

在JDK1.5中引入了「引數化型別」的概念,這個就是泛型,也就是說泛型和引數化型別是等價的,一回事,那麼我們來理解理解啥是引數化型別。

引數化型別

我們看看字面意思,引數化引數化,你想啊,我們有時候看一些魔幻電視劇,比如說某個人獸化了,大致知道啥意思吧,就是他變成了一個怪獸,變形了,哈哈,那麼這裡的引數化意思大概是不是就是變成了一個引數的意思呢?那麼後面還有一個型別,組合起來是不是就是「把型別變成了引數」,那型別是啥啊,不就是String型別,Integer型別這些嘛,現在把這些型別都作為了引數,這就是引數化型別了。

不知道我表達的是否清楚,你明白嗎?

好了,來看看泛型到底長啥樣吧:

List<String> stringList = new ArrayList<String>();

就是這個,我們經常這樣操作的,它就是泛型的應用,你看看它和如下沒有使用泛型的有啥區別:

List stringList = new ArrayList();

很容易看出,就是多了一個這個,那麼該如何進一步去理解它嘞?

理解泛型

還記得之前我們說的吧,泛型的引入很大一部分原因是為了讓集合能夠記住他裡面的元素的資料型別,怎麼讓它記住嘞,實際上實現也很簡單,就是隻傳入特定的型別,比如要傳入字串型別,那麼久只能傳入字串型別,像整型型別及其他型別就是不允許進入的,這樣的話就能保證集合中的元素都是統一的型別,集合自然就能記住了。

我們再來看之前舉的那個房子的例子:

通俗易懂吧,之前這個房子是開放的,誰都能進,現在嘞,我這個房子是用作狗屋的,自然是隻提供小狗來住,那麼其他的就不允許進入,你一個人讓你進你也不進啊,怎麼搞嘞,那就是在門口搞個檢查裝置,就好比安檢,首先告訴安檢,只能讓小狗進,其他的不讓進,於是乎,每過來一個,安檢都要檢查下是不是指定的小狗型別,不是的話不讓進,是的話就進去,如此一來這個屋子裡就都是小狗了,名副其實的狗屋啊。

那放到集合也是一樣的,現在要理解的就是如何給這集合加上安檢啊,再來看沒使用泛型的時候:

List stringList = new ArrayList();

這個時候是開放的,各個資料型別的都可以存放到這個List集合中,現在看下使用了泛型的:

List<String> stringList = new ArrayList<String>();

大家也看到了,就是多了個,我們在實際程式碼中看下:

image.png

image.png

顯而易見啊,報錯了,說是我需要一個String,你給傳入一個int,不符合要求,那就不能存入,所以啊,你看明白了嗎?List是一個集合,可以往裡面存入資料,現在要進行限制,不是什麼型別的都能存入,那就對存入的資料進行檢查,怎麼搞,那就在List後面加個,就成了List,看到沒,這個是不是就和我們上面舉例子中的那個安檢很像,負責檢查進來的資料,首先給它指定一個資料型別,這裡就是String,然後就是檢查,不是String的都不讓存進來,所以啊,這個就是一個安檢的作用啊,其中的String就是指定的資料型別,這就是泛型啊,也就是引數化型別,String是字串型別,這裡就作為一個引數放在這裡,儲存進List集合中的元素都要是String型別的。

咋樣,明白沒?這就是泛型啊,把String作為一個引數,型別引數化了,你看,是不是這麼回事。

這樣一看,是不是覺得泛型也挺好理解的,其實這只是對泛型的基本理解,泛型還是有不少內容的,在理解了泛型的基本概念之後,我們還需要看看泛型的其他內容,當然,我必須告訴你,即使上面我講的你明白了,接下來的內容同樣有可能讓你費解,一起來看下!

泛型進階

菱形語法

咋一看,這個很高階啊,啥意思嘞,其實看程式碼就知道怎麼回事,我們上面舉了這樣的集合泛型程式碼:

List<String> stringList = new ArrayList<String>();

你平常是這樣寫的嗎?我猜有這樣寫的:

List<String> stringList = new ArrayList<>();

有啥區別,很簡單,就是後面的ArrayList後面有尖括號內有沒有這個String,也就是型別引數,這個理解吧,按照我們上面說的,這裡加上是為了指明集合中元素的資料型別,那麼後面的不寫也行嗎?在Java7之前是必須寫的,也就是必須是這樣的形式:

List<String> stringList = new ArrayList<String>();

但是在Java7開始就可以這樣寫了:

List<String> stringList = new ArrayList<>();

因為在Java7中是可以通過前面的型別引數去推匯出ArrayList中的資料型別的,也就是型別引數不需要了,但是這個<>尖括號是必須的,至於尖括號中的型別,是可以自動被推匯出來的,這個就叫做菱形語法,為啥叫菱形語法嘞,因為這個<>尖括號像菱形啊……

理解型別引數(重點)

還記得什麼是型別引數嗎?看這行程式碼:

List<String> stringList = new ArrayList<>();

泛型的本質是引數化型別,就是把型別當做引數了,而這個型別引數就是尖括號內的東西了,在上面的這行程式碼中,所謂的型別引數就是這個String了,這點明白吧,另外啊,我們都是到Java中的方法中有形參和實參,這個都知道怎麼回事吧,那麼這裡型別引數其實也是有區別的,它也是分為型別形參和型別實參。

那啥是型別形參,啥又是型別實參啊?是不是覺得理解不了?彆著急,經過我下面的解釋,你就會覺得這是如此的簡單。

我們上面寫的這行程式碼:

List<String> stringList = new ArrayList<>();

這裡的型別引數String其實就是型別實參了,也就是實際的型別引數,這樣的型別引數其實就是各個資料型別,泛型不就是引數化型別嘛,型別都被當做引數使用,所以這裡的String其實就是實際的型別實參,是個字串型別,咋樣,理解吧?

那啥是型別形參呢?我們來看看List介面是如何定義的?

原始碼中List介面是這樣定義的,當然,看到尖括號,這就是泛型,只不過好像跟我們之前看的不太一一樣,之前這裡的尖括號都是具體的型別,比如String,這裡弄了一個E,這是啥玩意啊。

首先啊,你看,還記得Lsit集合人家是什麼都可以儲存的嘛,也就是整型啊,字串都可以,現在使用泛型之後,相當於你可以這樣寫程式碼:

List<String> stringList = new ArrayList<>();

那麼你這裡的List集合就只能儲存字串型別的,當然,如果我想讓List集合專門儲存整型資料呢?那是不是要這樣寫:

List<Integer> list = new ArrayList<>();

可是這樣的話,你發現問題沒?那我原始碼中的List該怎樣?現在要求就是我實際寫程式碼中List後面的泛型可以寫成各種資料型別,這就要求原始碼中的List定義必須是具備通用特性的,那就用個啥來做個抽象的,泛型中就是使用一些大寫的英文字母來作為型別形參,比如這裡的E就是一個型別形參,實際中你可以寫成String啊或者Interger,以便達到List集合只儲存特定型別資料的目的,而String這些就是型別實參了。

咋樣,明白吧,我之前學習這裡的時候也比較疑惑這個E是啥啊,想必大家也見過T,這都是啥,這些其實就是泛型中的型別形參,我們在建立具體的類,介面或者方法的時候可以把這些型別形參轉換成具體的型別形參,大家可以看看Map的定義,是這樣的:

這裡是一個K和一個V,所以啊這些都是有個基本命名的,大致有如下這些:

E 元素 集合框架使用
K 鍵 對映關係鍵的型別
V 值 對映關係中值得型別
N 數位 主要用於表示數位
T 通用型別1
S 通用型別1
U 通用型別1
V通用型別1

咋樣,看到這裡是不是有種豁然開朗的感覺呢?這些就是實際的型別形參,比如上面的Map,型別形參是這樣的:

Map<K,V>

實際的型別實參樣式是這樣的:

Map<String,String>

ok了吧!理解力型別引數的形參和實參,我們再看接下來的內容。

泛型使用

我們在上面介紹泛型的時候,基本上都是使用集合類來說明,這很大一部分原因是因為泛型的提出有相當大的原因是為了彌補集合的缺陷,當然我也說了,泛型絕不僅僅是侷限於集合,我們可以自定義泛型,比如自定義泛型介面和泛型類以及方法。

1、定義泛型介面

我們還是再來看下JDK中的List的定義,就是它:

這是定義了一個泛型的List介面,接下來我們來自己定義一個泛型介面,看看是怎麼定義的,來,上程式碼:

我們這裡自定義了一個泛型介面,這個泛型的型別形參是E,包含一個showTypeName方法,目的是列印出泛型實際型別實參的型別名稱,接下來寫一個類去實現這個泛型介面:

這裡要注意了,我們寫一個類,然後去實現上面我們自定義的泛型介面,這時候我們的類名後面也是要寫上泛型的,就是不能這樣,否則報錯:

然後就是實現泛型介面中的方法了,獲取傳入的型別實參的型別名稱,接下來我們使用這個類看下:

到這裡是不是就比較熟悉啦,跟我們之前一直舉例的List集合有點相似吧,我們看輸出結果:

得出我們輸入的型別形參型別是String型別,以上就是泛型介面定義的一個非常簡單的例子了,咋樣,不知道你看明白了嗎?

2、自定義泛型類

上面我們簡單說了下泛型介面的自定義,大致上就是你要知道泛型介面如何定義,類如何去實現一個泛型介面,接下來我門來看如何自定義一個泛型類,直接看程式碼:

這裡簡單自定義了一個泛型類,這裡要注意,我門上面說過了什麼是型別形參,並且介紹了幾個約定俗成的,實際上這裡的型別形參,你用任何一個大寫的英文字母都是ok的,比如這裡我就用了一個大寫的G,然後看看如何使用這個泛型類:

我們再來看看輸出結果:

咋樣,泛型類的自定義是不是也比較簡單,接下來我們看看,一個類如何去繼承一個泛型類。

3、繼承泛型類

直接看程式碼吧:

這裡就是直接繼承了上述我們實現的泛型類,這裡要注意了,我們繼承的泛型類,不能再是型別形參的形式了,也就是不能這個樣子:

也就是當你繼承了一個泛型類的時候,就要指定真實的型別實參,這個時候就要確定型別了。

4、原始型別

我們上面說了繼承泛型類的時候,後面不能再跟泛型形參的形式了,但是你是可以完全去除泛型的,也就是這樣的形式:

這樣也可以的,這被稱作是原始型別,那麼這樣的話,實際使用輸出是什麼呢?我們來使用這個類:

這時候其實就是把原先的型別形參G當做Object型別的了,而之前我們這樣的形式:

就相當於把原先的型別形參G全部當做是String型別了,所以你這裡就會報錯了:

因為這時候就只能傳入String了。

並不存在泛型類

我們拿JDK中的List來舉例說明,首先看下面的程式碼:

List<String> stringList = new ArrayList<>();

這行程式碼想必你已經非常熟悉了吧,再看個熟悉的程式碼:

List stringList = new ArrayList();

經過上面那麼多的講解,你肯定知道了,上面那種我們使用了泛型的形式,下面則沒有,也就是說本身List是什麼都可以儲存的,但是現在加上泛型之後,就只能儲存特定資料型別了,比如這裡的List,也就是從List變成了List了,但是實際上啊,這裡並沒有生成新的List,其實本質上還是List,只不過List做了規定,只能裝載String型別的資料,但是實際上List還是原來的那個List,所以啊,實現類ArrayList並不是一個新的類,不過可以看做是ArrayList的一個子類。

所以啊,泛型類,實際上是不存在的。

萬用字元

泛型中還有個萬用字元的概念,這個該怎麼理解呢?還是看例子比較好理解,來看程式碼:

假如這裡有這麼一個方法,也沒啥特別,要非說特別那就是方法中的引數是一個List,為啥特別,因為List是一個泛型介面啊,你看:

但是上面我們的List並沒有指定實際的型別實參,這樣就會產生一些問題,它會產生泛型警告的問題,那麼最好我們還是指定實際的型別形參,但是這裡也有問題啊,就是這裡我並不確定以後傳入過來的List的實際型別實參是啥,也就是說可能傳入這樣的一個List:

List<String> stringList = new ArrayList<>();

也有可能傳入這樣的一個List:

List<String> integerList = new ArrayList<>();

那該怎麼指定嘞?這樣嗎:

看著好像很對,我們試試:

很不幸,這裡報錯了,其實這裡一句話可以概括:

String是Object的子類,但是List卻不是List的子類

記住這個,就知道這裡肯定是不行的,那該怎麼辦嘞?這裡就要使用到萬用字元了,就是這樣:

就一個問號就搞定啦,這個時候我們使用這個Test方法的時候就是既可以傳入這樣的List:

List<String> stringList = new ArrayList<>();

也可以傳入這樣的List:

所以啊,使用萬用字元之後可以傳入的型別就多了,但是有的時候可能不需要傳入所有的,希望還是有一定的限制的,這個時候就需要萬用字元的上下限設定了,關於這個,限於篇幅問題,就不展開來講了。

泛型方法

這裡把泛型方法單獨拿出來講是覺得這個泛型方法理解起來還是需要費點勁兒的!實際情況也確實如此,泛型方法有點不好理解。

那啥是泛型方法嘞,簡單來說啊,泛型方法就是:

宣告方法的時候,可以定義一個或多個泛型形參

這裡我們拿泛型類來對比一下,對於泛型類,比如這個:

我們在範例化這個類的時候需要指明具體的型別實參,比如是String還是Intenger之類的,那麼對於泛型方法而言,它就是在定義的時候是泛型形參,而實際呼叫的時候需要指定泛型實參(也就是泛型的具體型別)

定義及使用泛型方法

接下來我們來看如何定義泛型,首先我們來看一個正常的方法定義:

這是一個很正常的方法定義,但是裡面的邏輯似乎不正常,為啥?覺得這樣沒必要啊,你看我們這裡需要給引數傳入的就是String型別,你這裡還輸出型別名稱,好像只有給你輸出來你才相信似的,好像是這兒回事,那我們接著來看,如何定義一個泛型方法。

你想啊,我們怎麼定義類,是不是有個關鍵字class,如何定義介面呢?是不是用關鍵字interface,所以,定義這些比較特殊的東西,必定有個特殊的東西,那麼如何定義泛型方法呢?你看:

這裡就簡單定義了一個泛型方法,這裡需要注意的就是,你憑啥說你定義的是一個泛型方法呢?這裡的一個象徵就是紅框中的泛型,也就是說啊,你要定義一個泛型方法的話,那麼你就必須在許可權修飾符(這裡是public)和返回值之間寫上,這個是泛型標誌,代表你這個是一個泛型方法,對了,這裡的T就是個泛型形參,也可以是其他的大寫字母,這個之前講過的,所以啊,你在看這個泛型方法,比如我們呼叫這個泛型方法:

是不是覺得邏輯沒啥問題啊,因為傳入的是個泛型,所以這裡通過一個泛型方法,可以傳入不同的型別,以便檢視其型別,這裡要注意了,我們寫的平常的方法,方法傳入的是確定的型別,但是使用泛型後,可以傳入各種型別,那麼就可以測試一些不知道是什麼型別的型別名稱了,可能有點繞,理解下!

所以啊,泛型方法的定義的語法如下:

修飾符 <T,E,……> 返回值型別 方法名稱(形參列表){
方法體……
}

我們來對照一下我們定義的泛型方法:

這裡的public就是修飾符,就是泛型列表,為啥說列表,其實是它可以包含多個泛型形參名稱,也就是那些個大寫的英文字母,後面的void就是返回值型別了,然後就是方法名稱和形參列表了。

重點總結

我們還是來看這個泛型方法的定義:

這裡有幾個知識點需要強調一下:

1、許可權修飾符和返回值中間的這個泛型列表,也即是很重要,就相當於是泛型方法的標誌,有了這個你才能叫做泛型方法
2、代表將使用泛型型別T,只有這個時候,你在方法中才可以使用泛型型別T
3、另外你需要知道的就是這個T啊就是泛型形參的命名,可以是T,E,K等等這些,反正就是大寫的英文字母就ok了

泛型方法的的學習建議

對於Java基礎中的泛型這塊,我一直覺得是個比較重要的知識點,當然,事實也是如此,所以這個知識點需要理解透徹了,但是另外一個現實情況就是,泛型的理解還是有一定門檻的,尤其對於初學者而言,那麼泛型中的泛型方法可能又稍微難理解一點,那麼對於這塊的學習,剛開始不建議死扣,有個基本認識即可,比如我上面介紹的那些關於泛型方法的基本知識點,剛開始瞭解這些即可,當然,泛型方法不僅僅是這些,只不過我們暫且不深入學習。

學習還是要一步步來,後續會單獨出文章詳細聊聊泛型方法的。

泛型擦除與轉換

這個泛型擦除是啥嘞?一般的話面試的時候要是問泛型的話,那就大概率會被問到這個泛型擦除了,先來看一段程式碼:

猜想一下,這兩個ArrayList的型別一樣嗎?看一下結果:

可以看到,這兩個是完全一樣的,也就是說,這裡的泛型實參String和Integer並沒有對ArrayList造成什麼本質上的影響,其實這裡蠻好理解的:

這裡的泛型就相當於一個安檢,指定了一個具體的資料型別,想要往這個集合中存入資料就得經過泛型這個安檢檢查,和指定資料型別不一樣的都不允許儲存,這就保證了儲存的型別都是指定的型別,也就是說啊,我這個集合只想儲存統一的資料型別,怎麼做嘞,那就搞個檢查裝置(就是泛型),起到一個過濾資料的作用,但是你這檢查裝置只是起到一個檢查過濾的作用,並不影響我本身,舉個例子就是好比我要開個針對程式設計師的大會,選了一個酒店,那麼這個酒店目前只允許程式設計師進入,所以找個保安(泛型)站在門口,只允許程式設計師進入,不是程式設計師的不讓進,那麼在大會開始之前(編譯階段),保安會在門口把門,只讓程式設計師進,等到人員都到場了,那麼可以確定的是酒店裡都是程式設計師了,那麼保安此時的任務就結束了,然後保安就可以撤了(泛型擦除),然後我們就開始開會了(執行階段)

這就是泛型擦除了,咋樣,我說的夠明白嗎?也就是說啊,泛型就是在編譯階段做了一個檢查,在編譯階段,不符合你指定的型別的資料都會檢查報錯,比如下面的程式碼:

ArrayList<String> stringArrayList = new ArrayList<>();

因為指定了具體的泛型實參String,那麼你這裡就只能新增字串,編譯階段是會檢查的,一旦你新增的不是字串,它就會報錯,你看:

等你新增完畢,這個ArrayList裡面就都是字串型別了,到了執行階段,這裡的泛型資訊就被擦除了,沒泛型啥事了,也就是本身ArrayList啥都可以儲存,現在是加了個泛型,可以確保我儲存的都是同種型別的。

再舉個例子就是,有一座茅草屋,本來這個茅草屋誰都可以進去,但是現在規定(相當於加了泛型),身價一個億的才能進去,但是即使進去的都是身價一個億的,這個茅草屋還是原來那個茅草屋,它也不可能變成豪華別墅啊(只是舉例子,拒絕槓精)

除了擦除,還有個轉換,舉個例子也就很容易明白,看程式碼:

這是我們之前自定義的一個簡單的泛型類,現在我們使用這個類:

我們這裡指定具體的泛型實參是String字串型別,那麼這個時候,程式碼的實際編譯階段,這個G就全部被替換成了String,也就是變成了這個樣子:

這就是一個型別的轉換了,說的簡單點就是你定義的泛型形參在編譯的階段會被全部替換成你實際指定的泛型實參。

總結

好了,限於文章的篇幅,關於泛型有些點並沒有詳細展開來講,或者有的點也沒講,對於知識的學習,是需要不斷的深入和反覆的迭代的,想要一次性把一個知識全部學完也是不可能的,只能說,需要不斷的學習,所以關於泛型的講解就到這了,下面我簡單說下我個人對泛型的一些理解吧,希望對你有錦上添花的作用。

其實我一直覺得泛型這個東西有點像保安,就是看大門的,按照你的規定,允許誰可以進,誰不可以進,當然,這裡的進不進就是可不可以儲存的問題了,比如集合,是否可以向其新增資料,這是在你給規定之後,而這個規定你可以制定的多種多樣,這麼一來,就靈活多變了,其實關於泛型吧,剛開始理解是一道坎,理解了就覺得通透了,但是理解了也不一定算是掌握了,後面看一些原始碼,使用泛型的很多,可是你還是不一定懂,但是在理解了的前提下,你才有可能讀懂那些原始碼,學習就是這樣,需要不斷的迭代,反覆的打磨,不能停啊!

好了,泛型就說到這裡。歡迎大家提出你的看法和建議,一起交流學習,文中若有不當之處,還望提出!