国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 之泛型通配符 ? extends T 與 ? super T 解惑

woshicixide / 2505人閱讀

摘要:簡述大家在平時的工作學習中肯定會見過不少如下的語句我們都知道上面的代碼時關于泛型的那么這兩個不同的寫法都有什么區別呢首先說到的泛型我們必須要提到的是泛型的類型擦除機制中的泛型基本上都是在編譯器這個層次來實現的在生成的字節代碼中是不包含泛型中

簡述

大家在平時的工作學習中, 肯定會見過不少如下的語句:

List
List

我們都知道, 上面的代碼時關于 Java 泛型的, 那么這兩個不同的寫法都有什么區別呢?

首先, 說到 Java 的泛型, 我們必須要提到的是Java 泛型的類型擦除機制: Java中的泛型基本上都是在編譯器這個層次來實現的. 在生成的 Java 字節代碼中是不包含泛型中的類型信息的. 使用泛型的時候加上的類型參數, 會被編譯器在編譯的時候去掉. 這個過程就稱為類型擦除. 如在代碼中定義的List和List等類型, 在編譯之后都會變成List, JVM看到的只是List, 而由泛型附加的類型信息對JVM來說是不可見的.

在使用泛型類時, 我們可以使用一個具體的類型, 例如可以定義一個 List 的對象, 我們的泛型參數就是 Integer; 我們也可以使用通配符 ? 來表示一個未知類型, 例如 List 就表示了泛型參數是某個類型, 只不過我們并不知道它的具體類型時什么.
List所聲明的就是所有類型都是可以的, 但需要注意的是, List并不等同于List. 對于 List 來說, 它實際上確定了 List 中包含的是 Object 及其子類, 我們可以使用 Object 類型來接收它的元素. 相對地, List 則表示其中所包含的元素類型是不確定, 其中可能包含的是 String, 也可能是 Integer. 如果它包含了 String 的話, 往里面添加 Integer 類型的元素就是錯誤的. 作為對比, 我們可以給一個 List 添加 String 元素, 也可以添加 Integer 類型的元素, 因為它們都是 Object 的子類.
正因為類型未知, 我們就不能通過 new ArrayList() 的方法來創建一個新的ArrayList 對象, 因為編譯器無法知道具體的類型是什么. 但是對于 List 中的元素, 我們卻都可以使用 Object 來接收, 因為雖然類型未知, 但肯定是Object及其子類.

我們在上面提到了, List 中的元素只能使用 Object 來引用, 這樣作肯定時不太方便的, 不過幸運的是, Java 的泛型機制允許我們對泛型參數的類型的上界和下界做一些限制, 例如 List 定義了泛型的上界是 Number, 即 List 中包含的元素類型是 Number 及其子類. 而 List 定義了泛型的下界, 即 List 中包含的是 Number 及其父類.
當引入了泛型參數的上界和下界后, 我們編寫代碼相對來說就方便了許多, 不過也引入了新的問題, 即我們在什么時候使用上界, 什么時候使用下界, 以及它們的區別和限制到底時什么? 下面我來說說我的理解.

? extends T

? extends T 描述了通配符上界, 即具體的泛型參數需要滿足條件: 泛型參數必須是 T 類型或它的子類, 例如:

List numberArray = new ArrayList();  // Number 是 Number 類型的
List numberArray = new ArrayList(); // Integer 是 Number 的子類
List numberArray = new ArrayList();  // Double 是 Number 的子類

上面三個操作都是合法的, 因為 ? extends Number 規定了泛型通配符的上界, 即我們實際上的泛型必須要是 Number 類型或者是它的子類, 而 Number, Integer, Double 顯然都是 Number 的子類(類型相同的也可以, 即這里我們可以認為 Number 是 Number 的子類).

子類型判斷

假設有類型 G, 以及 SuperClass 和 SubClass 兩個類, 并且 SuperClass 是 SubClass 的父類, 那么:

G 是 G 的子類型. 如 List 是 List 的子類型

G 是 G 的子類型, 例如 List 是 List 的子類型.

G 和 G 等同.

可以想象 G 為一個左閉右開的區間(T 在最左邊), G 是最大的區間, 當區間 G 包含 區間 G時, 那么較大的區間就是父類.

關于讀取

根據上面的例子, 對于 List numberArray 對象:

我們能夠從 numberArray 中讀取到 Number 對象, 因為 numberArray 中包含的元素是 Number 類型或 Number 的子類型.

我們不能從 numberArray 中讀取到 Integer 類型, 因為 numberArray 中可能保存的是 Double 類型.

同理, 我們也不能從 numberArray 中讀取到 Double 類型.

關于寫入

根據上面的例子, 對于 List numberArray 對象:

我們不能添加 Number 到 numberArray 中, 因為 numberArray 有可能是List 類型

我們不能添加 Integer 到 numberArray 中, 因為 numberArray 有可能是 List 類型

我們不能添加 Double 到 numberArray 中, 因為 numberArray 有可能是 List 類型

即, 我們不能添加任何對象到 List 中, 因為我們不能確定一個 List 對象實際的類型是什么, 因此就不能確定插入的元素的類型是否和這個 List 匹配. List 唯一能保證的是我們從這個 list 中讀取的元素一定是一個 T 類型的.

? super T

? super T 描述了通配符下界, 即具體的泛型參數需要滿足條件: 泛型參數必須是 T 類型或它的父類, 例如:

// 在這里, Integer 可以認為是 Integer 的 "父類"
List array = new ArrayList();
// Number 是 Integer 的 父類
List array = new ArrayList();
// Object 是 Integer 的 父類
List array = new ArrayList();
關于讀取

對于上面的例子中的 List array 對象:

我們不能保證可以從 array 對象中讀取到 Integer 類型的數據, 因為 array 可能是 List 類型的.

我們不能保證可以從 array 對象中讀取到 Number 類型的數據, 因為 array 可能是 List 類型的.

唯一能夠保證的是, 我們可以從 array 中獲取到一個 Object 對象的實例.

關于寫

對于上面的例子中的 List array 對象:

我們可以添加 Integer 對象到 array 中, 也可以添加 Integer 的子類對象到 array 中.

我們不能添加 Double/Number/Object 等不是 Integer 的子類的對象到 array 中.

易混淆點

有一點需要注意的是, ListList 中, 我們所說的 XX 是 T 的父類(a superclass of T)XX 是 T 的子類(a subclass of T) 其實是針對于泛型參數而言的. 例如考慮如下例子:

List l1 = ...
List l2 = ...

那么這里 ? super Integer? extends Integer 的限制是對誰的呢? 是表示我們可以插入任意的對象 X 到 l1 中, 只要 X 是 Integer 的父類? 是表示我們可以插入任意的對象 Y 到 l2 中, 只要 Y 是 Integer 的子類?
其實不是的, 我們必須要拋棄上面的概念, ? super Integer? extends Integer 限制的其實是 泛型參數, 即 List l1 表示 l1 的泛型參數 T 必須要滿足 T 是 Integer 的父類, 因此諸如 List, List 的對象就可以賦值到 l1 中. 正因為我們知道了 l1 中的泛型參數的邊界信息, 因此我們就可以向 l1 中添加 Integer 對象了, 推理過程如下:

令 T 是 l1 的泛型參數, 即:
    l1 = List = List
因此有 T 是 Integer 或 Integer 的父類.
如果 T 是 Integer, 則 l1 = List, 顯然我們可以添加任意的 Integer 對象或 Integer 的子類對象到 l1 中.
如果 T 是 Integer 的父類, 那么同理, 對于 Integer 或 Integer 的子類的對象, 我們也可以添加到 l1 中.

按同樣的分析方式, List l2 表示的是 l2 的泛型參數是 Integer 的子類型. 而如果我們要給一個 List 插入一個元素的話, 我們需要保證此元素是 T 或是 T 的子類, 而這里 List l2, l2 的泛型參數是什么類型我們都不知道, 進而就不能確定 l2 的泛型參數的子類是哪些, 因此我們就不能向 l2 中添加任何的元素了.

來一個對比:

對于 List l1:

正確的理解: ? super Integer 限定的是泛型參數. 令 l1 的泛型參數是 T, 則 T 是 Integer 或 Integer 的父類, 因此 Integer 或 Integer 的子類的對象就可以添加到 l1 中.

錯誤的理解: ? super Integer限定的是插入的元素的類型, 因此只要是 Integer 或 Integer 的父類的對象都可以插入 l1 中

對于 List l2:

正確的理解: ? extends Integer 限定的是泛型參數. 令 l2 的泛型參數是 T, 則 T 是 Integer 或 Integer 的子類, 進而我們就不能找到一個類 X, 使得 X 是泛型參數 T 的子類, 因此我們就不可以向 l2 中添加元素. 不過由于我們知道了泛型參數 T 是 Integer 或 Integer 的子類這一點, 因此我們就可以從 l2 中讀取到元素(取到的元素類型是 Integer 或 Integer 的子類), 并可以存放到 Integer 中.

錯誤的理解: ? extends Integer 限定的是插入元素的類型, 因此只要是 Integer 或 Integer 的子類的對象都可以插入 l2 中

使用場景

PECE 原則: Producer Extends, Consumer Super

Producer extends: 如果我們需要一個 List 提供類型為 T 的數據(即希望從 List 中讀取 T 類型的數據), 那么我們需要使用 ? extends T, 例如 List. 但是我們不能向這個 List 添加數據.

Consumer Super: 如果我們需要一個 List 來消費 T 類型的數據(即希望將 T 類型的數據寫入 List 中), 那么我們需要使用 ? super T, 例如 List. 但是這個 List 不能保證從它讀取的數據的類型.

如果我們既希望讀取, 也希望寫入, 那么我們就必須明確地聲明泛型參數的類型, 例如 List.

例子:

public class Collections { 
  public static  void copy(List dest, List src) 
  {
      for (int i=0; i

上面的例子是一個拷貝數據的代碼, src 是 List 類型的, 因此它可以讀取出 T 類型的數據(讀取的數據類型是 T 或是 T 的子類, 但是我們不能確切的知道它是什么類型, 唯一能確定的是讀取的類型 is instance of T), , dest 是 List 類型的, 因此它可以寫入 T 類型或其子類的數據.

參考

Java深度歷險(五)——Java泛型
difference-between-super-t-and-extends-t-in-java

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/66693.html

相關文章

  • Java系列泛型

    摘要:總結泛型的類型必須是引用類型,不能是基本類型,泛型的個數可以有多個,可以使用對創建對象時的泛型類型以及方法參數類型進行限制,如使用關鍵字和對泛型的具體類型進行向下限制或向上限制,最后一點,可以聲明泛型數組,但是不能創建泛型數組的實例。 自從 JDK 1.5 提供了泛型概念,泛型使得開發者可以定義較為安全的類型,不至于強制類型轉化時出現類型轉化異常,在沒有反省之前,可以通過 Object...

    MadPecker 評論0 收藏0
  • 泛型泛型

    摘要:定義具有一個或多個類型變量的類,稱之為泛型類。泛型類的繼承創建對象的兩種方式錯誤方式錯誤原因繼承了泛型類,但并不是泛型類,所以不能這樣創建對象。同樣是泛型類,它的父類也是泛型類,它傳遞的是常量。 泛型類 public class A { //在成員變量上使用泛型 private T t; public A() {} //構造參數類型上...

    caoym 評論0 收藏0
  • java編程思想》—— 泛型

    摘要:引用泛型除了方法因不能使用外部實例參數外,其他繼承實現成員變量,成員方法,方法返回值等都可使用。因此,生成的字節碼僅包含普通的類,接口和方法。 為什么要使用泛型程序設計? 一般的類和方法,只能使用具體的類型:要么是基本類型,要么是自定義類的對應類型;如果要編寫可以應用于多種類型的代碼,這種刻板的限制對代碼的束縛就會很大。----摘自原書Ordinary classes and meth...

    CODING 評論0 收藏0
  • Java? 教程(類型擦除)

    類型擦除 泛型被引入到Java語言中,以便在編譯時提供更嚴格的類型檢查并支持通用編程,為了實現泛型,Java編譯器將類型擦除應用于: 如果類型參數是無界的,則用它們的邊界或Object替換泛型類型中的所有類型參數,因此,生成的字節碼僅包含普通的類、接口和方法。 如有必要,插入類型轉換以保持類型安全。 生成橋接方法以保留擴展泛型類型中的多態性。 類型擦除確保不為參數化類型創建新類,因此,泛型不會...

    zsy888 評論0 收藏0
  • Java知識點總結(Java泛型

    摘要:知識點總結泛型知識點總結泛型泛型泛型就是參數化類型適用于多種數據類型執行相同的代碼泛型中的類型在使用時指定泛型歸根到底就是模版優點使用泛型時,在實際使用之前類型就已經確定了,不需要強制類型轉換。 Java知識點總結(Java泛型) @(Java知識點總結)[Java, Java泛型] [toc] 泛型 泛型就是參數化類型 適用于多種數據類型執行相同的代碼 泛型中的類型在使用時指定 泛...

    linkin 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<