亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

woshicixide / 2587人閱讀

摘要:簡(jiǎn)述大家在平時(shí)的工作學(xué)習(xí)中肯定會(huì)見過不少如下的語(yǔ)句我們都知道上面的代碼時(shí)關(guān)于泛型的那么這兩個(gè)不同的寫法都有什么區(qū)別呢首先說到的泛型我們必須要提到的是泛型的類型擦除機(jī)制中的泛型基本上都是在編譯器這個(gè)層次來實(shí)現(xiàn)的在生成的字節(jié)代碼中是不包含泛型中

簡(jiǎn)述

大家在平時(shí)的工作學(xué)習(xí)中, 肯定會(huì)見過不少如下的語(yǔ)句:

List
List

我們都知道, 上面的代碼時(shí)關(guān)于 Java 泛型的, 那么這兩個(gè)不同的寫法都有什么區(qū)別呢?

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

在使用泛型類時(shí), 我們可以使用一個(gè)具體的類型, 例如可以定義一個(gè) List 的對(duì)象, 我們的泛型參數(shù)就是 Integer; 我們也可以使用通配符 ? 來表示一個(gè)未知類型, 例如 List 就表示了泛型參數(shù)是某個(gè)類型, 只不過我們并不知道它的具體類型時(shí)什么.
List所聲明的就是所有類型都是可以的, 但需要注意的是, List并不等同于List. 對(duì)于 List 來說, 它實(shí)際上確定了 List 中包含的是 Object 及其子類, 我們可以使用 Object 類型來接收它的元素. 相對(duì)地, List 則表示其中所包含的元素類型是不確定, 其中可能包含的是 String, 也可能是 Integer. 如果它包含了 String 的話, 往里面添加 Integer 類型的元素就是錯(cuò)誤的. 作為對(duì)比, 我們可以給一個(gè) List 添加 String 元素, 也可以添加 Integer 類型的元素, 因?yàn)樗鼈兌际?Object 的子類.
正因?yàn)轭愋臀粗? 我們就不能通過 new ArrayList() 的方法來創(chuàng)建一個(gè)新的ArrayList 對(duì)象, 因?yàn)榫幾g器無法知道具體的類型是什么. 但是對(duì)于 List 中的元素, 我們卻都可以使用 Object 來接收, 因?yàn)殡m然類型未知, 但肯定是Object及其子類.

我們?cè)谏厦嫣岬搅? List 中的元素只能使用 Object 來引用, 這樣作肯定時(shí)不太方便的, 不過幸運(yùn)的是, Java 的泛型機(jī)制允許我們對(duì)泛型參數(shù)的類型的上界和下界做一些限制, 例如 List 定義了泛型的上界是 Number, 即 List 中包含的元素類型是 Number 及其子類. 而 List 定義了泛型的下界, 即 List 中包含的是 Number 及其父類.
當(dāng)引入了泛型參數(shù)的上界和下界后, 我們編寫代碼相對(duì)來說就方便了許多, 不過也引入了新的問題, 即我們?cè)谑裁磿r(shí)候使用上界, 什么時(shí)候使用下界, 以及它們的區(qū)別和限制到底時(shí)什么? 下面我來說說我的理解.

? extends T

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

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

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

子類型判斷

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

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

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

G 和 G 等同.

可以想象 G 為一個(gè)左閉右開的區(qū)間(T 在最左邊), G 是最大的區(qū)間, 當(dāng)區(qū)間 G 包含 區(qū)間 G時(shí), 那么較大的區(qū)間就是父類.

關(guān)于讀取

根據(jù)上面的例子, 對(duì)于 List numberArray 對(duì)象:

我們能夠從 numberArray 中讀取到 Number 對(duì)象, 因?yàn)?numberArray 中包含的元素是 Number 類型或 Number 的子類型.

我們不能從 numberArray 中讀取到 Integer 類型, 因?yàn)?numberArray 中可能保存的是 Double 類型.

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

關(guān)于寫入

根據(jù)上面的例子, 對(duì)于 List numberArray 對(duì)象:

我們不能添加 Number 到 numberArray 中, 因?yàn)?numberArray 有可能是List 類型

我們不能添加 Integer 到 numberArray 中, 因?yàn)?numberArray 有可能是 List 類型

我們不能添加 Double 到 numberArray 中, 因?yàn)?numberArray 有可能是 List 類型

即, 我們不能添加任何對(duì)象到 List 中, 因?yàn)槲覀儾荒艽_定一個(gè) List 對(duì)象實(shí)際的類型是什么, 因此就不能確定插入的元素的類型是否和這個(gè) List 匹配. List 唯一能保證的是我們從這個(gè) list 中讀取的元素一定是一個(gè) T 類型的.

? super T

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

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

對(duì)于上面的例子中的 List array 對(duì)象:

我們不能保證可以從 array 對(duì)象中讀取到 Integer 類型的數(shù)據(jù), 因?yàn)?array 可能是 List 類型的.

我們不能保證可以從 array 對(duì)象中讀取到 Number 類型的數(shù)據(jù), 因?yàn)?array 可能是 List 類型的.

唯一能夠保證的是, 我們可以從 array 中獲取到一個(gè) Object 對(duì)象的實(shí)例.

關(guān)于寫

對(duì)于上面的例子中的 List array 對(duì)象:

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

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

易混淆點(diǎn)

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

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

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

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

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

來一個(gè)對(duì)比:

對(duì)于 List l1:

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

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

對(duì)于 List l2:

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

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

使用場(chǎng)景

PECE 原則: Producer Extends, Consumer Super

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

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

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

例子:

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

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

參考

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

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/66693.html

相關(guān)文章

  • Java系列泛型

    摘要:總結(jié)泛型的類型必須是引用類型,不能是基本類型,泛型的個(gè)數(shù)可以有多個(gè),可以使用對(duì)創(chuàng)建對(duì)象時(shí)的泛型類型以及方法參數(shù)類型進(jìn)行限制,如使用關(guān)鍵字和對(duì)泛型的具體類型進(jìn)行向下限制或向上限制,最后一點(diǎn),可以聲明泛型數(shù)組,但是不能創(chuàng)建泛型數(shù)組的實(shí)例。 自從 JDK 1.5 提供了泛型概念,泛型使得開發(fā)者可以定義較為安全的類型,不至于強(qiáng)制類型轉(zhuǎn)化時(shí)出現(xiàn)類型轉(zhuǎn)化異常,在沒有反省之前,可以通過 Object...

    MadPecker 評(píng)論0 收藏0
  • 泛型泛型

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

    caoym 評(píng)論0 收藏0
  • java編程思想》—— 泛型

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

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

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

    zsy888 評(píng)論0 收藏0
  • Java知識(shí)點(diǎn)總結(jié)(Java泛型

    摘要:知識(shí)點(diǎn)總結(jié)泛型知識(shí)點(diǎn)總結(jié)泛型泛型泛型就是參數(shù)化類型適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼泛型中的類型在使用時(shí)指定泛型歸根到底就是模版優(yōu)點(diǎn)使用泛型時(shí),在實(shí)際使用之前類型就已經(jīng)確定了,不需要強(qiáng)制類型轉(zhuǎn)換。 Java知識(shí)點(diǎn)總結(jié)(Java泛型) @(Java知識(shí)點(diǎn)總結(jié))[Java, Java泛型] [toc] 泛型 泛型就是參數(shù)化類型 適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼 泛型中的類型在使用時(shí)指定 泛...

    linkin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<