<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Java 泛型 PECS - 生產者`extends`消費者`super` > 原文: [https://howtodoinjava.com/java/generics/java-generics-what-is-pecs-producer-extends-consumer-super/](https://howtodoinjava.com/java/generics/java-generics-what-is-pecs-producer-extends-consumer-super/) 昨天,我正在研究一些 [**Java 集合**](//howtodoinjava.com/java/collections/useful-java-collection-interview-questions/ "Useful java collection interview questions") API,并且發現了兩種主要用于將元素添加到集合中的方法。 他們倆都使用泛型語法來獲取方法參數。 但是,第一種方法是使用`<? super T>`,而第二種方法是使用`<? extends E>`。 為什么? 首先,讓我們看一下這兩種方法的完整語法。 此方法負責將集合`c`的所有成員添加到另一個調用此方法的集合中。 ```java boolean addAll(Collection<? extends E> c); ``` 調用此方法可將“元素”添加到集合`c`。 ```java public static <T> boolean addAll(Collection<? super T> c, T... elements); ``` 兩者似乎都在做簡單的事情,所以為什么它們都有不同的語法。 我們許多人可能會納悶。 在這篇文章中,我試圖揭開圍繞它的概念的神秘性,該概念主要被稱為 **PECS** (最早由 Joshua Bloch 在他的著作《Effective Java》中創造的術語)。 ## 為什么要使用泛型通配符? 在我與 [**java 泛型**](//howtodoinjava.com/java/generics/complete-java-generics-tutorial/ "Complete Java Generics Tutorial") 相關的上一篇文章中,我們了解到,泛型本質上用于**類型安全性和不變式**。 用例可以是 Integer 的列表,即`List<Integer>`。 如果您在 Java 中聲明`List<Integer>`之類的列表,則 Java 保證它將檢測并報告您將任何非整數類型插入上述列表的任何嘗試。 但是很多時候,我們面臨這樣的情況,為了特定的目的,我們必須在方法中傳遞類的子類型或超類型作為參數。 在這些情況下,我們必須使用協變(縮小引用)和逆變(擴大引用)之類的概念。 ## 了解`<? extends T>` 這是 **PECS** 的第一部分,即 **PE(生產者`extends`)**。 為了將其與現實生活中的術語聯系起來,讓我們使用一籃子水果(即水果的集合)的類比。 當我們從籃子里摘水果時,我們要確保只取出水果而沒有其他東西。 這樣我們就可以編寫如下泛型代碼: ```java Fruit get = fruits.get(0); ``` 在上述情況下,我們需要將水果的集合聲明為`List<? extends Fruit>`。 例如: ```java class Fruit { @Override public String toString() { return "I am a Fruit !!"; } } class Apple extends Fruit { @Override public String toString() { return "I am an Apple !!"; } } public class GenericsExamples { public static void main(String[] args) { //List of apples List<Apple> apples = new ArrayList<Apple>(); apples.add(new Apple()); //We can assign a list of apples to a basket of fruits; //because apple is subtype of fruit List<? extends Fruit> basket = apples; //Here we know that in basket there is nothing but fruit only for (Fruit fruit : basket) { System.out.println(fruit); } //basket.add(new Apple()); //Compile time error //basket.add(new Fruit()); //Compile time error } } ``` 查看上面的`for`循環。 它確保了從籃子里出來的任何東西都肯定會結出果實; 因此,您可以遍歷它,然后簡單地將其澆鑄成水果。 現在,在最后兩行中,我嘗試在購物籃中添加`Apple`和`Fruit`,但是編譯器不允許我添加。 為什么? 如果我們考慮一下,原因很簡單。 `<? extends Fruit>`通配符告訴編譯器我們正在處理水果類型的子類型,但是**我們無法知道哪個水果,因為可能存在多個子類型**。 由于沒有辦法說出來,而且我們需要保證類型安全(不變性),因此您不能在此類結構內放置任何內容。 另一方面,由于我們知道它可能是`Fruit`的子類型,因此可以從結構中獲取數據,并保證它是`Fruit`。 在上面的示例中,我們從集合`List<? extends Fruit> basket`中取出元素。所以這個籃子實際上是在生產水果。 簡而言之,當您只想從集合中檢索元素時,請將其視為生產者并使用`<? extends T>`語法。 “**生產者`extends`**”現在對您來說更有意義。 ## 了解`<? super T>` 現在以不同的方式查看上述用例。 假設我們正在定義一個方法,在此方法中,我們只會在此購物籃中添加不同的水果。 就像我們在帖子“ `addAll(Collection<? super T> c, T... elements)`”開頭看到的方法一樣。 在這種情況下,籃子用于存儲元素,因此應稱為元素的使用者。 現在看下面的代碼示例: ```java class Fruit { @Override public String toString() { return "I am a Fruit !!"; } } class Apple extends Fruit { @Override public String toString() { return "I am an Apple !!"; } } class AsianApple extends Apple { @Override public String toString() { return "I am an AsianApple !!"; } } public class GenericsExamples { public static void main(String[] args) { //List of apples List<Apple> apples = new ArrayList<Apple>(); apples.add(new Apple()); //We can assign a list of apples to a basket of apples List<? super Apple> basket = apples; basket.add(new Apple()); //Successful basket.add(new AsianApple()); //Successful basket.add(new Fruit()); //Compile time error } } ``` 我們可以在籃子內添加蘋果,甚至是亞洲蘋果,但不能在籃子中添加`Fruit`(蘋果的超類型)。 為什么? 原因是購物籃是對`Apple`的超類商品列表的引用。 同樣,**我們不知道它是**是哪個超類型,但是我們知道可以將`Apple`及其任何子類型(它們是`Fruit`的子類型)添加為沒有問題(您可以隨時在超類的集合中添加一個子類)。 因此,現在我們可以在購物籃中添加任何類型的`Apple`。 如何從這種類型的數據中獲取數據呢? 事實證明,您唯一可以使用的是`Object`實例:由于我們無法知道它是哪個超類型,因此編譯器只能保證它將是對`Object`的引用,因為`Object`是任何 Java 類型的超類型。 在上面的示例中,我們將元素放入集合`List<? super Apple> basket`; 所以在這里,這個籃子實際上是在消耗蘋果等元素。 簡而言之,當您只想在集合中添加元素時,請將其視為使用者并使用`<? super T>`語法。 現在,“**消費者`super`**”對您也應該更有意義。 ## 總結 基于上述推理和示例,讓我們總結要點。 1. 如果需要從集合中檢索類型`T`的對象,請使用`<? extends T>`通配符。 2. 如果需要將`T`類型的對象放入集合中,請使用`<? super T>`通配符。 3. 如果您需要同時滿足這兩個條件,請不要使用任何通配符。 就這么簡單。 4. 簡而言之,請記住術語 **PECS**。 生產者`extends`消費者`super`。 真的很容易記住。 這就是 Java 中泛型中簡單而又復雜的概念的全部。 通過評論讓我知道您的想法。 **祝您學習愉快!**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看