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

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## Chapter 5. Generics(泛型) ### Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數) Varargs methods (Item 53) and generics were both added to the platform in Java 5, so you might expect them to interact gracefully; sadly, they do not. The purpose of varargs is to allow clients to pass a variable number of arguments to a method, but it is a leaky abstraction: when you invoke a varargs method, an array is created to hold the varargs parameters; that array, which should be an implementation detail, is visible. As a consequence, you get confusing compiler warnings when varargs parameters have generic or parameterized types. 可變參數方法([Item-53](/Chapter-8/Chapter-8-Item-53-Use-varargs-judiciously.md))和泛型都是在 Java 5 中添加的,因此你可能認為它們能夠優雅地交互;可悲的是,他們并不能。可變參數的目的是允許客戶端向方法傳遞可變數量的參數,但這是一個抽象泄漏:當你調用可變參數方法時,將創建一個數組來保存參數;該數組本應是實現細節,卻是可見的。因此,當可變參數具有泛型或參數化類型時,會出現令人困惑的編譯器警告。 **譯注:有關「抽象泄漏」(Leaky Abstractions)的概念可參考 [奇舞精選 2021-07-06 的文章](https://mp.weixin.qq.com/s/KneomYX_7yQ78RzAmvIoHg)** Recall from Item 28 that a non-reifiable type is one whose runtime representation has less information than its compile-time representation, and that nearly all generic and parameterized types are non-reifiable. If a method declares its varargs parameter to be of a non-reifiable type, the compiler generates a warning on the declaration. If the method is invoked on varargs parameters whose inferred type is non-reifiable, the compiler generates a warning on the invocation too. The warnings look something like this: 回想一下 [Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md),非具體化類型是指其運行時表示的信息少于其編譯時表示的信息,并且幾乎所有泛型和參數化類型都是不可具體化的。如果方法聲明其可變參數為不可具體化類型,編譯器將在聲明上生成警告。如果方法是在其推斷類型不可具體化的可變參數上調用的,編譯器也會在調用時生成警告。生成的警告就像這樣: ``` warning: [unchecked] Possible heap pollution from parameterized vararg type List<String> ``` Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that type [JLS, 4.12.2]. It can cause the compiler’s automatically generated casts to fail, violating the fundamental guarantee of the generic type system. 當參數化類型的變量引用不屬于該類型的對象時,就會發生堆污染[JLS, 4.12.2]。它會導致編譯器自動生成的強制類型轉換失敗,違反泛型類型系統的基本保證。 For example, consider this method, which is a thinly disguised(偽裝的) variant of the code fragment on page 127: 例如,考慮這個方法,它摘自 127 頁([Item-26](/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md))的代碼片段,但做了些修改: ``` // Mixing generics and varargs can violate type safety! // 泛型和可變參數混合使用可能違反類型安全原則! static void dangerous(List<String>... stringLists) { List<Integer> intList = List.of(42); Object[] objects = stringLists; objects[0] = intList; // Heap pollution String s = stringLists[0].get(0); // ClassCastException } ``` This method has no visible casts yet throws a ClassCastException when invoked with one or more arguments. Its last line has an invisible cast that is generated by the compiler. This cast fails, demonstrating that type safety has been compromised, and **it is unsafe to store a value in a generic varargs array parameter.** 此方法沒有顯式的強制類型轉換,但在使用一個或多個參數調用時拋出 ClassCastException。它的最后一行有一個由編譯器生成的隱式強制轉換。此轉換失敗,表明類型安全性受到了影響,并且**在泛型可變參數數組中存儲值是不安全的。** This example raises an interesting question: Why is it even legal to declare a method with a generic varargs parameter, when it is illegal to create a generic array explicitly? In other words, why does the method shown previously generate only a warning, while the code fragment on page 127 generates an error? The answer is that methods with varargs parameters of generic or parameterized types can be very useful in practice, so the language designers opted to live with this inconsistency. In fact, the Java libraries export several such methods, including `Arrays.asList(T... a)`, `Collections.addAll(Collection<? super T> c, T... elements)`, and `EnumSet.of(E first, E... rest)`. Unlike the dangerous method shown earlier, these library methods are typesafe. 這個例子提出了一個有趣的問題:為什么使用泛型可變參數聲明方法是合法的,而顯式創建泛型數組是非法的?換句話說,為什么前面顯示的方法只生成警告,而 127 頁上的代碼片段發生錯誤?答案是,帶有泛型或參數化類型的可變參數的方法在實際開發中非常有用,因此語言設計人員選擇忍受這種不一致性。事實上,Java 庫導出了幾個這樣的方法,包括 `Arrays.asList(T... a)`、`Collections.addAll(Collection<? super T> c, T... elements)` 以及 `EnumSet.of(E first, E... rest)`。它們與前面顯示的危險方法不同,這些庫方法是類型安全的。 Prior to Java 7, there was nothing the author of a method with a generic varargs parameter could do about the warnings at the call sites. This made these APIs unpleasant to use. Users had to put up with the warnings or, preferably, to eliminate them with @SuppressWarnings("unchecked") annotations at every call site (Item 27). This was tedious, harmed readability, and hid warnings that flagged real issues. 在 Java 7 之前,使用泛型可變參數的方法的作者對調用點上產生的警告無能為力。使得這些 API 難以使用。用戶必須忍受這些警告,或者在每個調用點([Item-27](/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))使用 @SuppressWarnings("unchecked") 注釋消除這些警告。這種做法乏善可陳,既損害了可讀性,也忽略了標記實際問題的警告。 In Java 7, the SafeVarargs annotation was added to the platform, to allow the author of a method with a generic varargs parameter to suppress client warnings automatically. In essence, **the SafeVarargs annotation constitutes a promise by the author of a method that it is typesafe.** In exchange for this promise, the compiler agrees not to warn the users of the method that calls may be unsafe. 在 Java 7 中添加了 SafeVarargs 注釋,以允許使用泛型可變參數的方法的作者自動抑制客戶端警告。本質上,**SafeVarargs 注釋構成了方法作者的一個承諾,即該方法是類型安全的。** 作為這個承諾的交換條件,編譯器同意不對調用可能不安全的方法的用戶發出警告。 It is critical that you do not annotate a method with @SafeVarargs unless it actually is safe. So what does it take to ensure this? Recall that a generic array is created when the method is invoked, to hold the varargs parameters. If the method doesn’t store anything into the array (which would overwrite the parameters) and doesn’t allow a reference to the array to escape (which would enable untrusted code to access the array), then it’s safe. In other words, if the varargs parameter array is used only to transmit a variable number of arguments from the caller to the method—which is, after all, the purpose of varargs—then the method is safe. 關鍵問題是,使用 @SafeVarargs 注釋方法,該方法實際上應該是安全的。那么怎樣才能確保這一點呢?回想一下,在調用該方法時創建了一個泛型數組來保存可變參數。如果方法沒有將任何內容存儲到數組中(這會覆蓋參數),并且不允許對數組的引用進行轉義(這會使不受信任的代碼能夠訪問數組),那么它就是安全的。換句話說,如果可變參數數組僅用于將可變數量的參數從調用方傳輸到方法(畢竟這是可變參數的目的),那么該方法是安全的。 It is worth noting that you can violate type safety without ever storing anything in the varargs parameter array. Consider the following generic varargs method, which returns an array containing its parameters. At first glance, it may look like a handy little utility: 值得注意的是,在可變參數數組中不存儲任何東西就可能違反類型安全性。考慮下面的通用可變參數方法,它返回一個包含參數的數組。乍一看,它似乎是一個方便的小實用程序: ``` // UNSAFE - Exposes a reference to its generic parameter array! static <T> T[] toArray(T... args) { return args; } ``` This method simply returns its varargs parameter array. The method may not look dangerous, but it is! The type of this array is determined by the compiletime types of the arguments passed in to the method, and the compiler may not have enough information to make an accurate determination. Because this method returns its varargs parameter array, it can propagate heap pollution up the call stack. 這個方法只是返回它的可變參數數組。這種方法看起來并不危險,但確實危險!這個數組的類型由傳遞給方法的參數的編譯時類型決定,編譯器可能沒有足夠的信息來做出準確的決定。因為這個方法返回它的可變參數數組,所以它可以將堆污染傳播到調用堆棧上。 To make this concrete, consider the following generic method, which takes three arguments of type T and returns an array containing two of the arguments, chosen at random: 為了使其具體化,請考慮下面的泛型方法,該方法接受三個類型為 T 的參數,并返回一個包含隨機選擇的兩個參數的數組: ``` static <T> T[] pickTwo(T a, T b, T c) { switch(ThreadLocalRandom.current().nextInt(3)) { case 0: return toArray(a, b); case 1: return toArray(a, c); case 2: return toArray(b, c); } throw new AssertionError(); // Can't get here } ``` This method is not, in and of itself, dangerous and would not generate a warning except that it invokes the toArray method, which has a generic varargs parameter. 這個方法本身并不危險,并且不會生成警告,除非它調用 toArray 方法,該方法有一個通用的可變參數。 When compiling this method, the compiler generates code to create a varargs parameter array in which to pass two T instances to toArray. This code allocates an array of type Object[], which is the most specific type that is guaranteed to hold these instances, no matter what types of objects are passed to pickTwo at the call site. The toArray method simply returns this array to pickTwo, which in turn returns it to its caller, so pickTwo will always return an array of type Object[]. 編譯此方法時,編譯器生成代碼來創建一個可變參數數組,在該數組中向 toArray 傳遞兩個 T 實例。這段代碼分配了 type Object[] 的一個數組,這是保證保存這些實例的最特定的類型,無論調用站點上傳遞給 pickTwo 的是什么類型的對象。toArray 方法只是將這個數組返回給 pickTwo,而 pickTwo 又將這個數組返回給它的調用者,所以 pickTwo 總是返回一個 Object[] 類型的數組。 Now consider this main method, which exercises pickTwo: 現在考慮這個主要方法,練習 pickTwo: ``` public static void main(String[] args) { String[] attributes = pickTwo("Good", "Fast", "Cheap"); } ``` There is nothing at all wrong with this method, so it compiles without generating any warnings. But when you run it, it throws a ClassCastException, though it contains no visible casts. What you don’t see is that the compiler has generated a hidden cast to String[] on the value returned by pickTwo so that it can be stored in attributes. The cast fails, because Object[] is not a subtype of String[]. This failure is quite disconcerting because it is two levels removed from the method that actually causes the heap pollution (toArray), and the varargs parameter array is not modified after the actual parameters are stored in it. 這個方法沒有任何錯誤,因此它在編譯時不會生成任何警告。但是當你運行它時,它會拋出 ClassCastException,盡管它不包含可見的強制類型轉換。你沒有看到的是,編譯器在 pickTwo 返回的值上生成了一個隱藏的 String[] 轉換,這樣它就可以存儲在屬性中。轉換失敗,因為 Object[] 不是 String[] 的子類型。這個失敗非常令人不安,因為它是從方法中刪除了兩個導致堆污染的級別(toArray),并且可變參數數組在實際參數存儲在其中之后不會被修改。 This example is meant to drive home the point that **it is unsafe to give another method access to a generic varargs parameter array,** with two exceptions: it is safe to pass the array to another varargs method that is correctly annotated with @SafeVarargs, and it is safe to pass the array to a non-varargs method that merely computes some function of the contents of the array. 這個示例的目的是讓人明白,**讓另一個方法訪問泛型可變參數數組是不安全的**,只有兩個例外:將數組傳遞給另一個使用 @SafeVarargs 正確注釋的可變參數方法是安全的,將數組傳遞給僅計算數組內容的某個函數的非可變方法也是安全的。 Here is a typical example of a safe use of a generic varargs parameter. This method takes an arbitrary number of lists as arguments and returns a single list containing the elements of all of the input lists in sequence. Because the method is annotated with @SafeVarargs, it doesn’t generate any warnings, on the declaration or at its call sites: 下面是一個安全使用泛型可變參數的典型示例。該方法接受任意數量的列表作為參數,并返回一個包含所有輸入列表的元素的序列列表。因為該方法是用 @SafeVarargs 注釋的,所以它不會在聲明或調用點上生成任何警告: ``` // Safe method with a generic varargs parameter @SafeVarargs static <T> List<T> flatten(List<? extends T>... lists) { List<T> result = new ArrayList<>(); for (List<? extends T> list : lists) result.addAll(list); return result; } ``` The rule for deciding when to use the SafeVarargs annotation is simple: **Use @SafeVarargs on every method with a varargs parameter of a generic or parameterized type,** so its users won’t be burdened by needless and confusing compiler warnings. This implies that you should never write unsafe varargs methods like dangerous or toArray. Every time the compiler warns you of possible heap pollution from a generic varargs parameter in a method you control, check that the method is safe. As a reminder, a generic varargs methods is safe if: 決定何時使用 SafeVarargs 注釋的規則很簡單:**在每個帶有泛型或參數化類型的可變參數的方法上使用 @SafeVarargs**,這樣它的用戶就不會被不必要的和令人困惑的編譯器警告所困擾。這意味著你永遠不應該編寫像 dangerous 或 toArray 這樣不安全的可變參數方法。每當編譯器警告你控制的方法中的泛型可變參數可能造成堆污染時,請檢查該方法是否安全。提醒一下,一個通用的可變參數方法是安全的,如果: 1. it doesn’t store anything in the varargs parameter array, and 它沒有在可變參數數組中存儲任何東西,并且 2. it doesn’t make the array (or a clone) visible to untrusted code. If either of these prohibitions is violated, fix it. 它不會讓數組(或者其副本)出現在不可信的代碼中。如果違反了這些禁令中的任何一條,就糾正它。 Note that the SafeVarargs annotation is legal only on methods that can’t be overridden, because it is impossible to guarantee that every possible overriding method will be safe. In Java 8, the annotation was legal only on static methods and final instance methods; in Java 9, it became legal on private instance methods as well. 請注意,SafeVarargs 注釋僅對不能覆蓋的方法合法,因為不可能保證所有可能覆蓋的方法都是安全的。在 Java 8 中,注釋僅對靜態方法和最終實例方法合法;在 Java 9 中,它在私有實例方法上也成為合法的。 An alternative to using the SafeVarargs annotation is to take the advice of Item 28 and replace the varargs parameter (which is an array in disguise) with a List parameter. Here’s how this approach looks when applied to our flatten method. Note that only the parameter declaration has changed: 使用 SafeVarargs 注釋的另一種選擇是接受 [Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md) 的建議,并用 List 參數替換可變參數(它是一個偽裝的數組)。下面是將這種方法應用到我們的 flatten 方法時的效果。注意,只有參數聲明發生了更改: ``` // List as a typesafe alternative to a generic varargs parameter static <T> List<T> flatten(List<List<? extends T>> lists) { List<T> result = new ArrayList<>(); for (List<? extends T> list : lists) result.addAll(list); return result; } ``` This method can then be used in conjunction with the static factory method List.of to allow for a variable number of arguments. Note that this approach relies on the fact that the List.of declaration is annotated with @SafeVarargs: 然后可以將此方法與靜態工廠方法 List.of 一起使用,以允許可變數量的參數。注意,這種方法依賴于 List.of 聲明是用 @SafeVarargs 注釋的: ``` audience = flatten(List.of(friends, romans, countrymen)); ``` The advantage of this approach is that the compiler can prove that the method is typesafe. You don’t have to vouch for its safety with a SafeVarargs annotation, and you don’t have worry that you might have erred in determining that it was safe. The main disadvantage is that the client code is a bit more verbose and may be a bit slower. 這種方法的優點是編譯器可以證明該方法是類型安全的。你不必使用 SafeVarargs 注釋來保證它的安全性,也不必擔心在確定它的安全性時可能出錯。主要的缺點是客戶端代碼比較冗長,可能會比較慢。 This trick can also be used in situations where it is impossible to write a safe varargs method, as is the case with the toArray method on page 147. Its List analogue is the List.of method, so we don’t even have to write it; the Java libraries authors have done the work for us. The pickTwo method then becomes this: 這種技巧也可用于無法編寫安全的可變參數方法的情況,如第 147 頁中的 toArray 方法。它的列表類似于 List.of 方法,我們甚至不用寫;Java 庫的作者為我們做了這些工作。pickTwo 方法變成這樣: ``` static <T> List<T> pickTwo(T a, T b, T c) { switch(rnd.nextInt(3)) { case 0: return List.of(a, b); case 1: return List.of(a, c); case 2: return List.of(b, c); } throw new AssertionError(); } ``` and the main method becomes this: main 方法是這樣的: ``` public static void main(String[] args) { List<String> attributes = pickTwo("Good", "Fast", "Cheap"); } ``` The resulting code is typesafe because it uses only generics, and not arrays. 生成的代碼是類型安全的,因為它只使用泛型,而不使用數組。 In summary, varargs and generics do not interact well because the varargs facility is a leaky abstraction built atop arrays, and arrays have different type rules from generics. Though generic varargs parameters are not typesafe, they are legal. If you choose to write a method with a generic (or parameterized) varargs parameter, first ensure that the method is typesafe, and then annotate it with @SafeVarargs so it is not unpleasant to use. 總之,可變參數方法和泛型不能很好地交互,因為可變參數工具是構建在數組之上的漏洞抽象,并且數組具有與泛型不同的類型規則。雖然泛型可變參數不是類型安全的,但它們是合法的。如果選擇使用泛型(或參數化)可變參數編寫方法,首先要確保該方法是類型安全的,然后使用 @SafeVarargs 對其進行注釋,這樣使用起來就不會令人不愉快。 --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-5/Chapter-5-Introduction.md)** - **Previous Item(上一條目):[Item 31: Use bounded wildcards to increase API flexibility(使用有界通配符增加 API 的靈活性)](/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md)** - **Next Item(下一條目):[Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器)](/Chapter-5/Chapter-5-Item-33-Consider-typesafe-heterogeneous-containers.md)**
                  <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>

                              哎呀哎呀视频在线观看