<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 簡單泛型 促成泛型出現的最主要的動機之一是為了創建*集合類*,參見 [集合](book/12-Collections.md) 章節。集合用于存放要使用到的對象。數組也是如此,不過集合比數組更加靈活,功能更豐富。幾乎所有程序在運行過程中都會涉及到一組對象,因此集合是可復用性最高的類庫之一。 我們先看一個只能持有單個對象的類。這個類可以明確指定其持有的對象的類型: ```java // generics/Holder1.java class Automobile {} public class Holder1 { private Automobile a; public Holder1(Automobile a) { this.a = a; } Automobile get() { return a; } } ``` 這個類的可復用性不高,它無法持有其他類型的對象。我們可不希望為碰到的每個類型都編寫一個新的類。 在 Java 5 之前,我們可以讓這個類直接持有 `Object` 類型的對象: ```java // generics/ObjectHolder.java public class ObjectHolder { private Object a; public ObjectHolder(Object a) { this.a = a; } public void set(Object a) { this.a = a; } public Object get() { return a; } public static void main(String[] args) { ObjectHolder h2 = new ObjectHolder(new Automobile()); Automobile a = (Automobile)h2.get(); h2.set("Not an Automobile"); String s = (String)h2.get(); h2.set(1); // 自動裝箱為 Integer Integer x = (Integer)h2.get(); } } ``` 現在,`ObjectHolder` 可以持有任何類型的對象,在上面的示例中,一個 `ObjectHolder` 先后持有了三種不同類型的對象。 一個集合中存儲多種不同類型的對象的情況很少見,通常而言,我們只會用集合存儲同一種類型的對象。泛型的主要目的之一就是用來約定集合要存儲什么類型的對象,并且通過編譯器確保規約得以滿足。 因此,與其使用 `Object` ,我們更希望先指定一個類型占位符,稍后再決定具體使用什么類型。要達到這個目的,需要使用*類型參數*,用尖括號括住,放在類名后面。然后在使用這個類時,再用實際的類型替換此類型參數。在下面的例子中,`T` 就是類型參數: ```java // generics/GenericHolder.java public class GenericHolder<T> { private T a; public GenericHolder() {} public void set(T a) { this.a = a; } public T get() { return a; } public static void main(String[] args) { GenericHolder<Automobile> h3 = new GenericHolder<Automobile>(); h3.set(new Automobile()); // 此處有類型校驗 Automobile a = h3.get(); // 無需類型轉換 //- h3.set("Not an Automobile"); // 報錯 //- h3.set(1); // 報錯 } } ``` 創建 `GenericHolder` 對象時,必須指明要持有的對象的類型,將其置于尖括號內,就像 `main()` 中那樣使用。然后,你就只能在 `GenericHolder` 中存儲該類型(或其子類,因為多態與泛型不沖突)的對象了。當你調用 `get()` 取值時,直接就是正確的類型。 這就是 Java 泛型的核心概念:你只需告訴編譯器要使用什么類型,剩下的細節交給它來處理。 你可能注意到 `h3` 的定義非常繁復。在 `=` 左邊有 `GenericHolder<Automobile>`, 右邊又重復了一次。在 Java 5 中,這種寫法被解釋成“必要的”,但在 Java 7 中設計者修正了這個問題(新的簡寫語法隨后成為備受歡迎的特性)。以下是簡寫的例子: ```java // generics/Diamond.java class Bob {} public class Diamond<T> { public static void main(String[] args) { GenericHolder<Bob> h3 = new GenericHolder<>(); h3.set(new Bob()); } } ``` 注意,在 `h3` 的定義處,`=` 右邊的尖括號是空的(稱為“鉆石語法”),而不是重復左邊的類型信息。在本書剩余部分都會使用這種語法。 一般來說,你可以認為泛型和其他類型差不多,只不過它們碰巧有類型參數罷了。在使用泛型時,你只需要指定它們的名稱和類型參數列表即可。 ### 一個元組類庫 有時一個方法需要能返回多個對象。而 **return** 語句只能返回單個對象,解決方法就是創建一個對象,用它打包想要返回的多個對象。當然,可以在每次需要的時候,專門創建一個類來完成這樣的工作。但是有了泛型,我們就可以一勞永逸。同時,還獲得了編譯時的類型安全。 這個概念稱為*元組*,它是將一組對象直接打包存儲于單一對象中。可以從該對象讀取其中的元素,但不允許向其中存儲新對象(這個概念也稱為 *數據傳輸對象* 或 *信使* )。 通常,元組可以具有任意長度,元組中的對象可以是不同類型的。不過,我們希望能夠為每個對象指明類型,并且從元組中讀取出來時,能夠得到正確的類型。要處理不同長度的問題,我們需要創建多個不同的元組。下面是一個可以存儲兩個對象的元組: ```java // onjava/Tuple2.java package onjava; public class Tuple2<A, B> { public final A a1; public final B a2; public Tuple2(A a, B b) { a1 = a; a2 = b; } public String rep() { return a1 + ", " + a2; } @Override public String toString() { return "(" + rep() + ")"; } } ``` 構造函數傳入要存儲的對象。這個元組隱式地保持了其中元素的次序。 初次閱讀上面的代碼時,你可能認為這違反了 Java 編程的封裝原則。`a1` 和 `a2` 應該聲明為 **private**,然后提供 `getFirst()` 和 `getSecond()` 取值方法才對呀?考慮下這樣做能提供的“安全性”是什么:元組的使用程序可以讀取 `a1` 和 `a2` 然后對它們執行任何操作,但無法對 `a1` 和 `a2` 重新賦值。例子中的 `final` 可以實現同樣的效果,并且更為簡潔明了。 另一種設計思路是允許元組的用戶給 `a1` 和 `a2` 重新賦值。然而,采用上例中的形式無疑更加安全,如果用戶想存儲不同的元素,就會強制他們創建新的 `Tuple2` 對象。 我們可以利用繼承機制實現長度更長的元組。添加更多的類型參數就行了: ```java // onjava/Tuple3.java package onjava; public class Tuple3<A, B, C> extends Tuple2<A, B> { public final C a3; public Tuple3(A a, B b, C c) { super(a, b); a3 = c; } @Override public String rep() { return super.rep() + ", " + a3; } } // onjava/Tuple4.java package onjava; public class Tuple4<A, B, C, D> extends Tuple3<A, B, C> { public final D a4; public Tuple4(A a, B b, C c, D d) { super(a, b, c); a4 = d; } @Override public String rep() { return super.rep() + ", " + a4; } } // onjava/Tuple5.java package onjava; public class Tuple5<A, B, C, D, E> extends Tuple4<A, B, C, D> { public final E a5; public Tuple5(A a, B b, C c, D d, E e) { super(a, b, c, d); a5 = e; } @Override public String rep() { return super.rep() + ", " + a5; } } ``` 演示需要,再定義兩個類: ```java // generics/Amphibian.java public class Amphibian {} // generics/Vehicle.java public class Vehicle {} ``` 使用元組時,你只需要定義一個長度適合的元組,將其作為返回值即可。注意下面例子中方法的返回類型: ```java // generics/TupleTest.java import onjava.*; public class TupleTest { static Tuple2<String, Integer> f() { // 47 自動裝箱為 Integer return new Tuple2<>("hi", 47); } static Tuple3<Amphibian, String, Integer> g() { return new Tuple3<>(new Amphibian(), "hi", 47); } static Tuple4<Vehicle, Amphibian, String, Integer> h() { return new Tuple4<>(new Vehicle(), new Amphibian(), "hi", 47); } static Tuple5<Vehicle, Amphibian, String, Integer, Double> k() { return new Tuple5<>(new Vehicle(), new Amphibian(), "hi", 47, 11.1); } public static void main(String[] args) { Tuple2<String, Integer> ttsi = f(); System.out.println(ttsi); // ttsi.a1 = "there"; // 編譯錯誤,因為 final 不能重新賦值 System.out.println(g()); System.out.println(h()); System.out.println(k()); } } /* 輸出: (hi, 47) (Amphibian@1540e19d, hi, 47) (Vehicle@7f31245a, Amphibian@6d6f6e28, hi, 47) (Vehicle@330bedb4, Amphibian@2503dbd3, hi, 47, 11.1) */ ``` 有了泛型,你可以很容易地創建元組,令其返回一組任意類型的對象。 通過 `ttsi.a1 = "there"` 語句的報錯,我們可以看出,**final** 聲明確實可以確保 **public** 字段在對象被構造出來之后就不能重新賦值了。 在上面的程序中,`new` 表達式有些啰嗦。本章稍后會介紹,如何利用 *泛型方法* 簡化它們。 ### 一個堆棧類 接下來我們看一個稍微復雜一點的例子:堆棧。在 [集合](book/12-Collections.md) 一章中,我們用 `LinkedList` 實現了 `onjava.Stack` 類。在那個例子中,`LinkedList` 本身已經具備了創建堆棧所需的方法。`Stack` 是通過兩個泛型類 `Stack<T>` 和 `LinkedList<T>` 的組合來創建。我們可以看出,泛型只不過是一種類型罷了(稍后我們會看到一些例外的情況)。 這次我們不用 `LinkedList` 來實現自己的內部鏈式存儲機制。 ```java // generics/LinkedStack.java // 用鏈式結構實現的堆棧 public class LinkedStack<T> { private static class Node<U> { U item; Node<U> next; Node() { item = null; next = null; } Node(U item, Node<U> next) { this.item = item; this.next = next; } boolean end() { return item == null && next == null; } } private Node<T> top = new Node<>(); // 棧頂 public void push(T item) { top = new Node<>(item, top); } public T pop() { T result = top.item; if (!top.end()) { top = top.next; } return result; } public static void main(String[] args) { LinkedStack<String> lss = new LinkedStack<>(); for (String s : "Phasers on stun!".split(" ")) { lss.push(s); } String s; while ((s = lss.pop()) != null) { System.out.println(s); } } } ``` 輸出結果: ```java stun! on Phasers ``` 內部類 `Node` 也是一個泛型,它擁有自己的類型參數。 這個例子使用了一個 *末端標識* (end sentinel) 來判斷棧何時為空。這個末端標識是在構造 `LinkedStack` 時創建的。然后,每次調用 `push()` 就會創建一個 `Node<T>` 對象,并將其鏈接到前一個 `Node<T>` 對象。當你調用 `pop()` 方法時,總是返回 `top.item`,然后丟棄當前 `top` 所指向的 `Node<T>`,并將 `top` 指向下一個 `Node<T>`,除非到達末端標識,這時就不能再移動 `top` 了。如果已經到達末端,程序還繼續調用 `pop()` 方法,它只能得到 `null`,說明棧已經空了。 ### RandomList 作為容器的另一個例子,假設我們需要一個持有特定類型對象的列表,每次調用它的 `select()` 方法時都隨機返回一個元素。如果希望這種列表可以適用于各種類型,就需要使用泛型: ```java // generics/RandomList.java import java.util.*; import java.util.stream.*; public class RandomList<T> extends ArrayList<T> { private Random rand = new Random(47); public T select() { return get(rand.nextInt(size())); } public static void main(String[] args) { RandomList<String> rs = new RandomList<>(); Arrays.stream("The quick brown fox jumped over the lazy brown dog".split(" ")).forEach(rs::add); IntStream.range(0, 11).forEach(i -> System.out.print(rs.select() + " ")); } } ``` 輸出結果: ```java brown over fox quick quick dog brown The brown lazy brown ``` `RandomList` 繼承了 `ArrayList` 的所有方法。本例中只添加了 `select()` 這個方法。
                  <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>

                              哎呀哎呀视频在线观看