天天看點

總結筆記(一) - 泛型總結泛型總結

文章目錄

  • 泛型總結
    • 泛型是什麼?
    • Java 是如何實作泛型的?
    • 什麼是泛型的類型擦除呢?
    • 泛型的通配符 `?`、`extends` 和 `super`
      • ?通配符
      • extends 通配符
      • super 通配符
      • 小結
      • 參考資料

泛型總結

泛型是什麼?

簡單說就是類型參數化,什麼意思呢?參數化的意思就是我們在定義的時候不知道具體的值,我們在到我們實際運作的時候才知道具體的值。類型參數化就是具體類型在定義的時候不知道,在實際運作的時候是确定的某一個類型。

Java 是如何實作泛型的?

泛型是很多進階語言都有的特性。根據定義,泛型在運作時表示同一個類型,我們比較容易想到

List<A>

List<B>

用 2 個不同的 Class 表示,這個是可行的,但是 Java 由于需要相容支援舊的代碼,而且在推出泛型前就提供了容器類,這種方式(

List<A>

List<B>

用 2 個不同的 Class)無法相容以前的老代碼,是以這個實作方法不适用。是以 Java 大佬們想了另外一種方式來實作泛型,這種方式就是

類型擦除

什麼是泛型的類型擦除呢?

類型擦除就是在實際生成位元組碼的時候,編譯器源碼裡面定義的

List<A>

變成了

List<Object>

,源碼裡面定義的

A Class

擦除

了,變成了

Object

,同時在使用的時候,會強制類型轉換,把取出來的

object

轉成

A

的執行個體去使用。這就是類型擦除。

初步看,泛型擦除好像是沒什麼大的問題,但是仔細想想,在強制類型轉換的時候,由于會丢掉類型的一些資訊,會導緻一些不符合預期的事情。比如有個基類 A,和它的兩個子類 B 和 C ,然後我們有下面的一段代碼。

List<A> listA = new ArrayList<A>();
listA.add(new B()); // 錯誤的,
           

第二行代碼是不符合預期的,因為 list 裡面期望放的是 A 而不是 B。 但是這個好像不太符合預期,我們有時候希望子類是可以放進容器裡面的。但是如果支援這個操作的話,會發生什麼呢?取出來來的是 B 還是 C ?如果不能明确,那麼就沒有實作“泛型”。

為了解決這個問題, Java 大佬們想了個方法,提出了一些通配符來解決這些問題。

泛型的通配符

?

extends

super

在了解通配符之前,我們需要知道的是,通配符的發明是為了解決什麼問題?至少要解決的一個問題是:容器裡面放進去的是什麼,取出來的就是什麼。

這個問題,其實分兩步,放進去,是說放進去同一種類型的東西。取出來,是說取出同一種類型的東西。或者說,用到通配符的地方應該是在不同的地方,一個地方把資料寫到容器,另外一個地方把資料從容器拿出來,如果實在同一個代碼塊裡寫入和讀取資料到同一個容器,應該是知道具體類型的,是不需要用到通配符的。

?通配符

?

通配符稱為無限通配符,表示不确定或者不關心類型。

extends 通配符

一般稱為上界通配符,表示的意思是:取值範圍為 (某個類的子類, 某個類]。再想想我們之前說的,通配符要解決的問題?放進去的是什麼,取出來的就應該是什麼。放資料和取資料應用在不同的場景。

通過上面的表述,容易推斷出來

<? extends E>

的集合隻能往外拿資料,因為取出來的一定是

E

,但是放進去的不知道是什麼,可能是

E

,也可能是

E

的子類,如果允許往集合裡面放東西,就不能保證放進去的是什麼,拿出來的就是什麼了。因為隻能保證拿出來的是

E

super 通配符

一般稱為下界通配符,表示的意思是:取值範圍為 [某個類,這個類的父類)。結合上面小節的解釋,可以推斷出

<? super S>

的集合隻能往裡面放資料,而不能從裡面拿東西,為什麼呢?因為

<? extends E>

解決的就是拿出來的問題啊,是以這個解決的就是放進去的問題啊,囧。裡面放的是下限或者下限的子類。

小結

通配符與一個規則,

PE-CS

  • PE

    是說,如果某個集合表示一個生産者,應該用

    extends

    通配符。因為生産者有上限,比如生産筆的公司,上限就是能生産筆,但是不能生産布。
  • CS

    是說,如果某個集合表示一個消費者,應該用

    super

    通配符。因為消費者是有下限的,好比去買筆,購物車裡面可以放鉛筆、鋼筆或者毛筆。這些下限就是筆,就是說都是筆。
  • 同時作為生産者和消費者的情況不存在,因為你可以指定具體的泛型。

參考資料

  • 深入了解 Java 泛型