<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 泛型時會出現的各類問題。 ### 任何基本類型都不能作為類型參數 正如本章早先提到的,Java 泛型的限制之一是不能將基本類型用作類型參數。因此,不能創建 `ArrayList<int>` 之類的東西。 解決方法是使用基本類型的包裝器類以及自動裝箱機制。如果創建一個 `ArrayList<Integer>`,并將基本類型 **int** 應用于這個集合,那么你將發現自動裝箱機制將自動地實現 **int** 到 **Integer** 的雙向轉換——因此,這幾乎就像是有一個 `ArrayList<int>` 一樣: ```java // generics/ListOfInt.java // Autoboxing compensates for the inability // to use primitives in generics import java.util.*; import java.util.stream.*; public class ListOfInt { public static void main(String[] args) { List<Integer> li = IntStream.range(38, 48) .boxed() // Converts ints to Integers .collect(Collectors.toList()); System.out.println(li); } } /* Output: [38, 39, 40, 41, 42, 43, 44, 45, 46, 47] */ ``` 通常,這種解決方案工作得很好——能夠成功地存儲和讀取 **int**,自動裝箱隱藏了轉換的過程。但是如果性能成為問題的話,就需要使用專門為基本類型適配的特殊版本的集合;一個開源版本的實現是 **org.apache.commons.collections.primitives**。 下面是另外一種方式,它可以創建持有 **Byte** 的 **Set**: ```java // generics/ByteSet.java import java.util.*; public class ByteSet { Byte[] possibles = { 1,2,3,4,5,6,7,8,9 }; Set<Byte> mySet = new HashSet<>(Arrays.asList(possibles)); // But you can't do this: // Set<Byte> mySet2 = new HashSet<>( // Arrays.<Byte>asList(1,2,3,4,5,6,7,8,9)); } ``` 自動裝箱機制解決了一些問題,但并沒有解決所有問題。 在下面的示例中,**FillArray** 接口包含一些通用方法,這些方法使用 **Supplier** 來用對象填充數組(這使得類泛型在本例中無法工作,因為這個方法是靜態的)。**Supplier** 實現來自 [數組](book/21-Arrays.md) 一章,并且在 `main()` 中,可以看到 `FillArray.fill()` 使用對象填充了數組: ```java // generics/PrimitiveGenericTest.java import onjava.*; import java.util.*; import java.util.function.*; // Fill an array using a generator: interface FillArray { static <T> T[] fill(T[] a, Supplier<T> gen) { Arrays.setAll(a, n -> gen.get()); return a; } static int[] fill(int[] a, IntSupplier gen) { Arrays.setAll(a, n -> gen.getAsInt()); return a; } static long[] fill(long[] a, LongSupplier gen) { Arrays.setAll(a, n -> gen.getAsLong()); return a; } static double[] fill(double[] a, DoubleSupplier gen) { Arrays.setAll(a, n -> gen.getAsDouble()); return a; } } public class PrimitiveGenericTest { public static void main(String[] args) { String[] strings = FillArray.fill( new String[5], new Rand.String(9)); System.out.println(Arrays.toString(strings)); int[] integers = FillArray.fill( new int[9], new Rand.Pint()); System.out.println(Arrays.toString(integers)); } } /* Output: [btpenpccu, xszgvgmei, nneeloztd, vewcippcy, gpoalkljl] [635, 8737, 3941, 4720, 6177, 8479, 6656, 3768, 4948] */ ``` 自動裝箱不適用于數組,因此我們必須創建 `FillArray.fill()` 的重載版本,或創建產生 **Wrapped** 輸出的生成器。 **FillArray** 僅比 `java.util.Arrays.setAll()` 有用一點,因為它返回填充的數組。 ### 實現參數化接口 一個類不能實現同一個泛型接口的兩種變體,由于擦除的原因,這兩個變體會成為相同的接口。下面是產生這種沖突的情況: ```java // generics/MultipleInterfaceVariants.java // {WillNotCompile} package generics; interface Payable<T> {} class Employee implements Payable<Employee> {} class Hourly extends Employee implements Payable<Hourly> {} ``` **Hourly** 不能編譯,因為擦除會將 `Payable<Employe>` 和 `Payable<Hourly>` 簡化為相同的類 **Payable**,這樣,上面的代碼就意味著在重復兩次地實現相同的接口。十分有趣的是,如果從 **Payable** 的兩種用法中都移除掉泛型參數(就像編譯器在擦除階段所做的那樣)這段代碼就可以編譯。 在使用某些更基本的 Java 接口,例如 `Comparable<T>` 時,這個問題可能會變得十分令人惱火,就像你在本節稍后看到的那樣。 ### 轉型和警告 使用帶有泛型類型參數的轉型或 **instanceof** 不會有任何效果。下面的集合在內部將各個值存儲為 **Object**,并在獲取這些值時,再將它們轉型回 **T**: ```java // generics/GenericCast.java import java.util.*; import java.util.stream.*; class FixedSizeStack<T> { private final int size; private Object[] storage; private int index = 0; FixedSizeStack(int size) { this.size = size; storage = new Object[size]; } public void push(T item) { if(index < size) storage[index++] = item; } @SuppressWarnings("unchecked") public T pop() { return index == 0 ? null : (T)storage[--index]; } @SuppressWarnings("unchecked") Stream<T> stream() { return (Stream<T>)Arrays.stream(storage); } } public class GenericCast { static String[] letters = "ABCDEFGHIJKLMNOPQRS".split(""); public static void main(String[] args) { FixedSizeStack<String> strings = new FixedSizeStack<>(letters.length); Arrays.stream("ABCDEFGHIJKLMNOPQRS".split("")) .forEach(strings::push); System.out.println(strings.pop()); strings.stream() .map(s -> s + " ") .forEach(System.out::print); } } /* Output: S A B C D E F G H I J K L M N O P Q R S */ ``` 如果沒有 **@SuppressWarnings** 注解,編譯器將對 `pop()` 產生 “unchecked cast” 警告。由于擦除的原因,編譯器無法知道這個轉型是否是安全的,并且 `pop()` 方法實際上并沒有執行任何轉型。 這是因為,**T** 被擦除到它的第一個邊界,默認情況下是 **Object** ,因此 `pop()` 實際上只是將 **Object** 轉型為 **Object**。 有時,泛型沒有消除對轉型的需要,這就會由編譯器產生警告,而這個警告是不恰當的。例如: ```java // generics/NeedCasting.java import java.io.*; import java.util.*; public class NeedCasting { @SuppressWarnings("unchecked") public void f(String[] args) throws Exception { ObjectInputStream in = new ObjectInputStream( new FileInputStream(args[0])); List<Widget> shapes = (List<Widget>)in.readObject(); } } ``` 正如你將在 [附錄:對象序列化](book/Appendix-Object-Serialization.md) 中學到的那樣,`readObject()` 無法知道它正在讀取的是什么,因此它返回的是必須轉型的對象。但是當注釋掉 **@SuppressWarnings** 注解并編譯這個程序時,就會得到下面的警告。 ``` NeedCasting.java uses unchecked or unsafe operations. Recompile with -Xlint:unchecked for details. And if you follow the instructions and recompile with - Xlint:unchecked :(如果遵循這條指示,使用-Xlint:unchecked來重新編譯:) NeedCasting.java:10: warning: [unchecked] unchecked cast List<Widget> shapes = (List<Widget>)in.readObject(); required: List<Widget> found: Object 1 warning ``` 你會被強制要求轉型,但是又被告知不應該轉型。為了解決這個問題,必須使用 Java 5 引入的新的轉型形式,既通過泛型類來轉型: ```java // generics/ClassCasting.java import java.io.*; import java.util.*; public class ClassCasting { @SuppressWarnings("unchecked") public void f(String[] args) throws Exception { ObjectInputStream in = new ObjectInputStream( new FileInputStream(args[0])); // Won't Compile: // List<Widget> lw1 = // List<>.class.cast(in.readObject()); List<Widget> lw2 = List.class.cast(in.readObject()); } } ``` 但是,不能轉型到實際類型( `List<Widget>` )。也就是說,不能聲明: ``` List<Widget>.class.cast(in.readobject()) ``` 甚至當你添加一個像下面這樣的另一個轉型時: ``` (List<Widget>)List.class.cast(in.readobject()) ``` 仍舊會得到一個警告。 ### 重載 下面的程序是不能編譯的,即使它看起來是合理的: ```java // generics/UseList.java // {WillNotCompile} import java.util.*; public class UseList<W, T> { void f(List<T> v) {} void f(List<W> v) {} } ``` 因為擦除,所以重載方法產生了相同的類型簽名。 因而,當擦除后的參數不能產生唯一的參數列表時,你必須提供不同的方法名: ```java // generics/UseList2.java import java.util.*; public class UseList2<W, T> { void f1(List<T> v) {} void f2(List<W> v) {} } ``` 幸運的是,編譯器可以檢測到這類問題。 ### 基類劫持接口 假設你有一個實現了 **Comparable** 接口的 **Pet** 類: ```java // generics/ComparablePet.java public class ComparablePet implements Comparable<ComparablePet> { @Override public int compareTo(ComparablePet o) { return 0; } } ``` 嘗試縮小 **ComparablePet** 子類的比較類型是有意義的。例如,**Cat** 類可以與其他的 **Cat** 比較: ```java // generics/HijackedInterface.java // {WillNotCompile} class Cat extends ComparablePet implements Comparable<Cat> { // error: Comparable cannot be inherited with // different arguments: <Cat> and <ComparablePet> // class Cat // ^ // 1 error public int compareTo(Cat arg) { return 0; } } ``` 不幸的是,這不能工作。一旦 **Comparable** 的類型參數設置為 **ComparablePet**,其他的實現類只能比較 **ComparablePet**: ```java // generics/RestrictedComparablePets.java public class Hamster extends ComparablePet implements Comparable<ComparablePet> { @Override public int compareTo(ComparablePet arg) { return 0; } } // Or just: class Gecko extends ComparablePet { public int compareTo(ComparablePet arg) { return 0; } } ``` **Hamster** 顯示了重新實現 **ComparablePet** 中相同的接口是可能的,只要接口完全相同,包括參數類型。然而正如 **Gecko** 中所示,這與直接覆寫基類的方法完全相同。
                  <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>

                              哎呀哎呀视频在线观看