<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 泛型教程 > 原文: [https://howtodoinjava.com/java/generics/complete-java-generics-tutorial/](https://howtodoinjava.com/java/generics/complete-java-generics-tutorial/) Java 中的[泛型](https://docs.oracle.com/javase/tutorial/extra/generics/ "generics")作為 JDK 5 的特性之一引入。就個人而言,我發現泛型中使用的尖括號`<>`非常吸引人,它總是使我不得不重新思考我在哪里使用它,或查看它是否以其他人的代碼編寫。 坦率地說,很長一段時間以來我一直在使用泛型,但我仍然不完全有信心盲目使用它。 在本教程中,我將介紹對 **java 泛型**有用的所有內容以及與它們有關的內容。 如果您認為我可以在教程的任何部分使用更精確的詞,或者可以添加示例,或者您不同意我的觀點; 給我留言。 我很高興知道您的觀點。 ```java Table of content 1) Why Generics? 2) How Generics works in Java 3) Types of Generics? i) Generic Type Class or Interface ii) Generic Type Method or Constructor 4) Generic Type Arrays 5) Generics with Wildcards i) Unbounded Wildcards ii) Bounded Wildcards a) Upper Bounded Wildcards b) Lower Bounded Wildcards 6) What is not allowed to do with Generics? ``` “**Java 泛型**”是一個技術術語,表示與泛型類型和方法的定義和使用有關的一組語言特性。 在 Java 中,泛型類型或方法與常規類型和方法的不同之處在于它們具有類型參數。 > “Java 泛型是一種語言特性,允許定義和使用泛型類型和方法。” 通過提供替代正式類型參數的實際類型參數,實例化泛型類型以形成參數化類型。 像`LinkedList<E>`這樣的類是泛型,其類型參數為 E。 諸如`LinkedList<Integer>`或`LinkedList<String>`之類的實例稱為參數化類型,而`String`和`Integer`是各自的實際類型參數。 ## 1)為什么要泛型? 如果仔細觀察 [**Java 集合框架**](//howtodoinjava.com/java/collections/useful-java-collection-interview-questions/ "Useful java collection interview questions")類,則您會發現大多數類都采用`Object`類型的參數/參數,并從方法中返回`Object`作為值。 現在,以這種形式,他們可以將任何 Java 類型用作參數并返回相同的值。 它們本質上是異構的,即不是特定的相似類型。 像我們這樣的程序員經常想指定一個集合只包含某種類型的元素,例如 `Integer`或`String`或`Employee`。 在原始的集合框架中,如果在代碼中添加一些檢查之前沒有添加額外的檢查,就不可能擁有同類收集。 引入泛型來消除此限制是非常具體的。 他們會在編譯時自動在代碼中添加這種類型的參數檢查。 這可以節省我們編寫大量不必要的代碼的時間,如果編寫正確的話,這些代碼實際上不會在運行時添加任何值。 > “用通俗易懂的術語來說,泛型使用 Java 語言強制類型安全。” 沒有這種類型的安全性,您的代碼可能會感染各種錯誤,而這些錯誤只會在運行時才被發現。 使用泛型,使它們在編譯時本身突出顯示,甚至在獲得 Java 源代碼文件的字節碼之前,也使代碼健壯。 > “泛型通過在編譯時檢測到更多錯誤來增加代碼的穩定性。” 因此,現在我們有了一個合理的想法,為什么泛型首先出現在 java 中。 下一步是了解有關它們如何在 Java 中工作的知識。 在源代碼中使用泛型時實際發生的情況。 ## 2)泛型在 Java 中的工作方式 泛型的核心是“[**類型安全性**](https://en.wikipedia.org/wiki/Type_safety "type safety")”。 類型安全到底是什么? 編譯器只是保證,如果在正確的位置使用正確的類型,則運行時不應有任何`ClassCastException`。 用例可以是`Integer`的列表,即`List<Integer>`。 如果您在 Java 中聲明`List<Integer>`之類的列表,則 Java 保證它將檢測并報告您將任何非整數類型插入上述列表的任何嘗試。 Java 泛型中的另一個重要術語是“[**類型擦除**](https://en.wikipedia.org/wiki/Type_erasure "type erasure")”。 從本質上講,這意味著使用泛型添加到源代碼中的所有額外信息將從其生成的字節碼中刪除。 在字節碼內部,它將是舊的 Java 語法,如果您根本不使用泛型,則會得到該語法。 當未在語言中添加泛型時,這必然有助于生成和執行在 Java 5 之前編寫的代碼。 讓我們看一個例子。 ```java List<Integer> list = new ArrayList<Integer>(); list.add(1000); //works fine list.add("lokesh"); //compile time error; ``` 當您編寫上述代碼并進行編譯時,會出現以下錯誤:“類型為`List<Integer>`的`add(Integer)`方法不適用于參數`(String)`”。 編譯器警告您。 這正是泛型的唯一目的,即類型安全。 第二部分是從上述示例中刪除第二行后獲得字節碼。 如果將上面的示例的字節碼與泛型進行比較,則沒有任何區別。 顯然,編譯器刪除了所有泛型信息。 因此,上面的代碼與沒有泛型的下面的代碼非常相似。 ```java List list = new ArrayList(); list.add(1000); ``` > “準確地說,Java 中的泛型不過是類型安全代碼的語法糖,所有此類類型信息都將由編譯器的類型擦除特性擦除。” ## 3)泛型的類型? 現在我們對泛型的意義有了一些了解。 現在開始探索圍繞泛型的其他重要概念。 我將從確定各種方法開始,泛型可以應用于源代碼。 #### 泛型類或接口 如果一個類聲明一個或多個類型變量,則它是泛型的。 這些類型變量稱為類的類型參數。 讓我們看一個例子。 `DemoClass`是簡單的 Java 類,具有一個屬性`t`(也可以不止一個); 屬性的類型是`Object`。 ```java class DemoClass { private Object t; public void set(Object t) { this.t = t; } public Object get() { return t; } } ``` 在這里,我們希望一旦使用某種類型初始化了該類,則該類應僅與該特定類型一起使用。 例如如果我們要讓一個類的實例保存類型為“`String`”的值`t`,那么程序員應該設置并獲取唯一的`String`類型。 由于我們已將屬性類型聲明為`Object`,因此無法強制執行此限制。 程序員可以設置任何對象。 由于所有 Java 類型都是`Object`類的子類型,因此可以從`get`方法獲得任何返回值類型。 要強制執行此類型限制,我們可以使用以下泛型: ```java class DemoClass<T> { //T stands for "Type" private T t; public void set(T t) { this.t = t; } public T get() { return t; } } ``` 現在我們可以放心,不會將類誤用于錯誤的類型。 `DemoClass`的用法示例如下所示: ```java DemoClass<String> instance = new DemoClass<String>(); instance.set("lokesh"); //Correct usage instance.set(1); //This will raise compile time error ``` 上面的類比也適用于接口。 讓我們快速看一個示例,以了解如何在 Java 接口中使用泛型類型信息。 ```java //Generic interface definition interface DemoInterface<T1, T2> { T2 doSomeOperation(T1 t); T1 doReverseOperation(T2 t); } //A class implementing generic interface class DemoClass implements DemoInterface<String, Integer> { public Integer doSomeOperation(String t) { //some code } public String doReverseOperation(Integer t) { //some code } } ``` 我希望我足夠清楚地介紹泛型類和接口。 現在該看一下泛型方法和構造器了。 #### 泛型方法或構造器 泛型方法與泛型類非常相似。 它們僅在一個方面不同,即類型信息的范圍僅在方法(或構造器)內部。 泛型方法是引入自己的類型參數的方法。 讓我們通過一個例子來理解這一點。 下面是一種泛型方法的代碼示例,該方法可用于在該類型的變量列表中查找所有出現的類型參數。 ```java public static <T> int countAllOccurrences(T[] list, T item) { int count = 0; if (item == null) { for ( T listItem : list ) if (listItem == null) count++; } else { for ( T listItem : list ) if (item.equals(listItem)) count++; } return count; } ``` 如果在此方法中傳遞`String`的列表和另一個字符串進行搜索,它將可以正常工作。 但是,如果嘗試在`String`列表中找到`Number`,則會出現編譯時錯誤。 與上述相同可以作為泛型構造器的示例。 讓我們也為泛型構造器提供一個單獨的示例。 ```java class Dimension<T> { private T length; private T width; private T height; //Generic constructor public Dimension(T length, T width, T height) { super(); this.length = length; this.width = width; this.height = height; } } ``` 在此示例中,`Dimension`類的構造器也具有類型信息。 因此,您可以僅具有單一類型的所有屬性的維度實例。 ## 4)泛型數組 任何語言中的數組都具有相同的含義,即數組是元素類型相似的集合。 在 Java 中,在運行時將任何不兼容的類型推入數組將拋出`ArrayStoreException`。 這意味著數組在運行時保留其類型信息,而泛型使用類型擦除或在運行時刪除任何類型信息。 由于上述沖突,不允許在 Java 中實例化泛型數組。 ```java public class GenericArray<T> { // this one is fine public T[] notYetInstantiatedArray; // causes compiler error; Cannot create a generic array of T public T[] array = new T[5]; } ``` 與上述泛型類型類和方法相同,我們可以在 java 中擁有泛型數組。 如我們所知,數組是元素類型相似的集合,并且推送任何不兼容的類型都會在運行時拋出`ArrayStoreException`; `Collection`類不是這種情況。 ```java Object[] array = new String[10]; array[0] = "lokesh"; array[1] = 10; //This will throw ArrayStoreException ``` 犯上述錯誤并不是很難。 它可以隨時發生。 因此,最好也將類型信息提供給數組,以便在編譯時就捕獲錯誤。 數組不支持泛型的另一個原因是數組是協變的,這意味著超類型引用的數組是子類型引用的數組的超類型。 也就是說,`Object[]`是`String[]`的超類型,并且可以通過類型`Object[]`的引用變量訪問字符串數組。 ```java Object[] objArr = new String[10]; // fine objArr[0] = new String(); ``` ## 5)帶有通配符的泛型 在泛型代碼中,稱為通配符的問號(`?`)表示未知類型。 **通配符參數化類型是泛型類型的實例,其中至少一個類型參數是通配符。** 通配符參數化類型的示例是`Collection<?>`,`List<? extends Number>`,`Comparator<? super String>`和`Pair<String, ?>`。 通配符可以在多種情況下使用:作為參數,字段或局部變量的類型; 有時作為返回類型(盡管更具體的做法是更好的編程習慣)。 通配符從不用作泛型方法調用,泛型類實例創建或超類型的類型參數。 在不同的地方使用通配符也具有不同的含義。 例如 * `Collection<?>`表示`Collection`接口的所有實例化,與類型參數無關。 * `List<? extends Number>`表示所有列表類型,其中元素類型是`Number`的子類型。 * `Comparator<? super String>`表示`Comparator`接口的所有實例化,這些實例化類型是`String`的超類型。 通配符參數化類型不是可能出現在新表達式中的具體類型。 它只是暗示了 Java 泛型所執行的規則,即在使用通配符的任何特定情況下,哪種類型均有效。 例如,以下是涉及通配符的有效聲明: ```java Collection<?> coll = new ArrayList<String>(); //OR List<? extends Number> list = new ArrayList<Long>(); //OR Pair<String,?> pair = new Pair<String,Integer>(); ``` 以下是通配符的無效用法,它們將給出編譯時錯誤。 ```java List<? extends Number> list = new ArrayList<String>(); //String is not subclass of Number; so error //OR Comparator<? super String> cmp = new RuleBasedCollator(new Integer(100)); //Integer is not superclass of String ``` 泛型中的通配符可以是無界的,也可以是有界的。 讓我們找出不同方面的差異。 #### 無界通配符參數化類型 一種泛型類型,其中所有類型參數都是無界通配符`?`,對類型變量沒有任何限制。 例如 ```java ArrayList<?> list = new ArrayList<Long>(); //or ArrayList<?> list = new ArrayList<String>(); //or ArrayList<?> list = new ArrayList<Employee>(); ``` #### 有界通配符參數化類型 有界通配符對可能的類型施加了一些限制,您可以用來實例化參數化類型。 使用關鍵字`super`和`extends`強制執行此限制。 為了更清楚地區分,我們將它們分為上限通配符和下限通配符。 ##### 上限通配符 例如,假設您要編寫一種適用于`List<String>`,`List<Integer>`和`List<Double>`的方法,則可以使用上限通配符來實現,例如您將指定`List<? extends Number>`。 這里的`Integer`,`Double`是`Number`類的子類型。 用通俗易懂的話來說,如果您希望泛型表達式接受特定類型的所有子類,則可以使用`extends`關鍵字來使用上限通配符。 ```java public class GenericsExample<T> { public static void main(String[] args) { //List of Integers List<Integer> ints = Arrays.asList(1,2,3,4,5); System.out.println(sum(ints)); //List of Doubles List<Double> doubles = Arrays.asList(1.5d,2d,3d); System.out.println(sum(doubles)); List<String> strings = Arrays.asList("1","2"); //This will give compilation error as :: The method sum(List<? extends Number>) in the //type GenericsExample<T> is not applicable for the arguments (List<String>) System.out.println(sum(strings)); } //Method will accept private static Number sum (List<? extends Number> numbers){ double s = 0.0; for (Number n : numbers) s += n.doubleValue(); return s; } } ``` ##### 下界通配符 如果您希望泛型表達式接受所有類型,即特定類型的“超”類型或特定類的父類,則可以使用`super`關鍵字使用下界通配符。 在下面給出的示例中,我創建了三個類,即`SuperClass`,`ChildClass`和`GrandChildClass`。 下面的代碼顯示了這種關系。 現在,我們必須創建一個以某種方式(例如從 DB)獲取`GrandChildClass`信息并創建其實例的方法。 我們希望將此新的`GrandChildClass`存儲在`GrandChildClasses`的現有列表中。 這里的問題是`GrandChildClass`是`ChildClass`和`SuperClass`的子類型。 因此,任何`SuperClasses`和`ChildClasses`的泛型列表都可以容納`GrandChildClasses`。 在這里,我們必須使用`super`關鍵字來使用下界通配符。 ```java package test.core; import java.util.ArrayList; import java.util.List; public class GenericsExample<T> { public static void main(String[] args) { //List of grand children List<GrandChildClass> grandChildren = new ArrayList<GrandChildClass>(); grandChildren.add(new GrandChildClass()); addGrandChildren(grandChildren); //List of grand childs List<ChildClass> childs = new ArrayList<ChildClass>(); childs.add(new GrandChildClass()); addGrandChildren(childs); //List of grand supers List<SuperClass> supers = new ArrayList<SuperClass>(); supers.add(new GrandChildClass()); addGrandChildren(supers); } public static void addGrandChildren(List<? super GrandChildClass> grandChildren) { grandChildren.add(new GrandChildClass()); System.out.println(grandChildren); } } class SuperClass{ } class ChildClass extends SuperClass{ } class GrandChildClass extends ChildClass{ } ``` ## 6)不允許使用泛型? 到目前為止,我們已經了解了許多可使用 Java 中的泛型來避免應用程序中許多`ClassCastException`實例的方法。 我們還看到了通配符的用法。 現在是時候確定一些 Java 泛型中不允許執行的任務。 #### a)您不能使用類型的靜態字段 您不能在類中定義靜態的泛型參數化成員。 這樣做的任何嘗試都會產生編譯時錯誤:無法靜態引用非靜態類型`T`。 ```java public class GenericsExample<T> { private static T member; //This is not allowed } ``` #### b)您不能創建`T`的實例 任何創建`T`實例的嘗試都會失敗,并顯示以下錯誤:無法實例化類型`T`。 ```java public class GenericsExample<T> { public GenericsExample(){ new T(); } } ``` #### c)泛型與聲明中的原始類型不兼容 對,是真的。 您不能聲明泛型表達式,例如`List`或`Map <String,double>`。 絕對可以使用包裝器類代替原始類型,然后在傳遞實際值時使用原始類型。 通過使用自動裝箱將原始類型轉換為相應的包裝器類,可以接受這些值原始類型。 ```java final List<int> ids = new ArrayList<>(); //Not allowed final List<Integer> ids = new ArrayList<>(); //Allowed ``` #### d)您無法創建泛型異常類 有時,程序員可能需要傳遞泛型類型的實例以及引發異常。 在 Java 中這是不可能的。 ```java // causes compiler error public class GenericException<T> extends Exception {} ``` 當您嘗試創建這樣的異常時,您將得到如下消息:泛型類`GenericException`可能不是子類`java.lang.Throwable`。 到此為止,這一切都結束了,這次是關于 **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>

                              哎呀哎呀视频在线观看