<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、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## Chapter 5. Generics(泛型) ### Item 33: Consider typesafe heterogeneous containers(考慮類型安全的異構容器) Common uses of generics include collections, such as `Set<E>` and `Map<K,V>`, and single-element containers, such as `ThreadLocal<T>` and `AtomicReference<T>`. In all of these uses, it is the container that is parameterized. This limits you to a fixed number of type parameters per container. Normally that is exactly what you want. A Set has a single type parameter, representing its element type; a Map has two, representing its key and value types; and so forth. 集合是泛型的常見應用之一,如 `Set<E>` 和 `Map<K,V>`,以及單元素容器,如 `ThreadLocal<T>` 和 `AtomicReference<T>`。在所有這些應用中,都是參數化的容器。這將每個容器的類型參數限制為固定數量。通常這正是你想要的。Set 只有一個類型參數,表示其元素類型;Map 有兩個,表示`鍵`和`值`的類型;如此等等。 Sometimes, however, you need more flexibility. For example, a database row can have arbitrarily many columns, and it would be nice to be able to access all of them in a typesafe manner. Luckily, there is an easy way to achieve this effect. The idea is to parameterize the key instead of the container. Then present the parameterized key to the container to insert or retrieve a value. The generic type system is used to guarantee that the type of the value agrees with its key. 然而,有時你需要更大的靈活性。例如,一個數據庫行可以有任意多列,能夠以類型安全的方式訪問所有這些列是很好的。幸運的是,有一種簡單的方法可以達到這種效果。其思想是參數化`鍵`而不是容器。然后向容器提供參數化`鍵`以插入或檢索`值`。泛型類型系統用于確保`值`的類型與`鍵`一致。 As a simple example of this approach, consider a Favorites class that allows its clients to store and retrieve a favorite instance of arbitrarily many types. The Class object for the type will play the part of the parameterized key. The reason this works is that class Class is generic. The type of a class literal is not simply Class, but `Class<T>`. For example, String.class is of type `Class<String>`, and Integer.class is of type `Class<Integer>`. When a class literal is passed among methods to communicate both compiletime and runtime type information, it is called a type token [Bracha04]. 作為這種方法的一個簡單示例,考慮一個 Favorites 類,它允許客戶端存儲和檢索任意多種類型的 Favorites 實例。Class 類的對象將扮演參數化`鍵`的角色。這樣做的原因是 Class 類是泛型的。Class 對象的類型不僅僅是 Class,而是 `Class<T>`。例如,String.class 的類型為 `Class<String>`、Integer.class 的類型為 `Class<Integer>`。在傳遞編譯時和運行時類型信息的方法之間傳遞類 Class 對象時,它被稱為類型標記[Bracha04]。 The API for the Favorites class is simple. It looks just like a simple map, except that the key is parameterized instead of the map. The client presents a Class object when setting and getting favorites. Here is the API: Favorites 類的 API 很簡單。它看起來就像一個簡單的 Map,只不過`鍵`是參數化的,而不是整個 Map。客戶端在設置和獲取 Favorites 實例時顯示一個 Class 對象。以下是 API: ``` // Typesafe heterogeneous container pattern - API public class Favorites { public <T> void putFavorite(Class<T> type, T instance); public <T> T getFavorite(Class<T> type); } ``` Here is a sample program that exercises the Favorites class, storing, retrieving, and printing a favorite String, Integer, and Class instance: 下面是一個示例程序,它演示了 Favorites 類、存儲、檢索和打印 Favorites 字符串、整數和 Class 實例: ``` // Typesafe heterogeneous container pattern - client public static void main(String[] args) { Favorites f = new Favorites(); f.putFavorite(String.class, "Java"); f.putFavorite(Integer.class, 0xcafebabe); f.putFavorite(Class.class, Favorites.class); String favoriteString = f.getFavorite(String.class); int favoriteInteger = f.getFavorite(Integer.class); Class<?> favoriteClass = f.getFavorite(Class.class); System.out.printf("%s %x %s%n", favoriteString,favoriteInteger, favoriteClass.getName()); } ``` As you would expect, this program prints Java cafebabe Favorites. Note, incidentally, that Java’s printf method differs from C’s in that you should use %n where you’d use \n in C. The %n generates the applicable platform-specific line separator, which is \n on many but not all platforms. 如你所料,這個程序打印 Java cafebabe Favorites。順便提醒一下,Java 的 printf 方法與 C 的不同之處在于,你應該在 C 中使用 \n 的地方改用 %n。 **譯注:`favoriteClass.getName()` 的打印結果與 Favorites 類所在包名有關,結果應為:包名.Favorites** A Favorites instance is typesafe: it will never return an Integer when you ask it for a String. It is also heterogeneous: unlike an ordinary map, all the keys are of different types. Therefore, we call Favorites a typesafe heterogeneous container. Favorites 的實例是類型安全的:當你向它請求一個 String 類型時,它永遠不會返回一個 Integer 類型。它也是異構的:與普通 Map 不同,所有`鍵`都是不同類型的。因此,我們將 Favorites 稱為一個類型安全異構容器。 The implementation of Favorites is surprisingly tiny. Here it is, in its entirety: Favorites 的實現非常簡短。下面是全部內容: ``` // Typesafe heterogeneous container pattern - implementation public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<>(); public <T> void putFavorite(Class<T> type, T instance) { favorites.put(Objects.requireNonNull(type), instance); } public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } } ``` There are a few subtle things going on here. Each Favorites instance is backed by a private `Map<Class<?>, Object>` called favorites. You might think that you couldn’t put anything into this Map because of the unbounded wildcard type, but the truth is quite the opposite. The thing to notice is that the wildcard type is nested: it’s not the type of the map that’s a wildcard type but the type of its key. This means that every key can have a different parameterized type: one can be `Class<String>`, the next `Class<Integer>`, and so on. That’s where the heterogeneity comes from. 這里發生了一些微妙的事情。每個 Favorites 實例都由一個名為 favorites 的私有 `Map<Class<?>, Object>` 支持。你可能認為由于通配符類型是無界的,所以無法將任何內容放入此映射中,但事實恰恰相反。需要注意的是,通配符類型是嵌套的:通配符類型不是 Map 的類型,而是`鍵`的類型。這意味著每個`鍵`都可以有不同的參數化類型:一個可以是 `Class<String>`,下一個是 `Class<Integer>`,等等。這就是異構的原理。 The next thing to notice is that the value type of the favorites Map is simply Object. In other words, the Map does not guarantee the type relationship between keys and values, which is that every value is of the type represented by its key. In fact, Java’s type system is not powerful enough to express this. But we know that it’s true, and we take advantage of it when the time comes to retrieve a favorite. 接下來要注意的是 favorites 的`值`類型僅僅是 Object。換句話說,Map 不保證`鍵`和`值`之間的類型關系,即每個`值`都是其`鍵`所表示的類型。實際上,Java 的類型系統還沒有強大到足以表達這一點。但是我們知道這是事實,當需要檢索一個 favorite 時,我們會利用它。 The putFavorite implementation is trivial: it simply puts into favorites a mapping from the given Class object to the given favorite instance. As noted, this discards the “type linkage” between the key and the value; it loses the knowledge that the value is an instance of the key. But that’s OK, because the getFavorites method can and does reestablish this linkage. putFavorite 的實現很簡單:它只是將從給定 Class 對象到給定 Favorites 實例的放入 favorites 中。如前所述,這將丟棄`鍵`和`值`之間的「類型關聯」;將無法確定`值`是`鍵`的實例。但這沒關系,因為 getFavorites 方法可以重新建立這個關聯。 The implementation of getFavorite is trickier than that of putFavorite. First, it gets from the favorites map the value corresponding to the given Class object. This is the correct object reference to return, but it has the wrong compile-time type: it is Object (the value type of the favorites map) and we need to return a T. So, the getFavorite implementation dynamically casts the object reference to the type represented by the Class object, using Class’s cast method. getFavorite 的實現比 putFavorite 的實現更復雜。首先,它從 favorites 中獲取與給定 Class 對象對應的`值`。這是正確的對象引用返回,但它有錯誤的編譯時類型:它是 Object(favorites 的`值`類型),我們需要返回一個 T。因此,getFavorite 的實現通過使用 Class 的 cast 方法,將對象引用類型動態轉化為所代表的 Class 對象。 The cast method is the dynamic analogue of Java’s cast operator. It simply checks that its argument is an instance of the type represented by the Class object. If so, it returns the argument; otherwise it throws a ClassCastException. We know that the cast invocation in getFavorite won’t throw ClassCastException, assuming the client code compiled cleanly. That is to say, we know that the values in the favorites map always match the types of their keys. cast 方法是 Java 的 cast 運算符的動態模擬。它只是檢查它的參數是否是類對象表示的類型的實例。如果是,則返回參數;否則它將拋出 ClassCastException。我們知道 getFavorite 中的強制轉換調用不會拋出 ClassCastException,假設客戶端代碼已正確地編譯。也就是說,我們知道 favorites 中的`值`總是與其`鍵`的類型匹配。 So what does the cast method do for us, given that it simply returns its argument? The signature of the cast method takes full advantage of the fact that class Class is generic. Its return type is the type parameter of the Class object: 如果 cast 方法只是返回它的參數,那么它會為我們做什么呢?cast 方法的簽名充分利用了 Class 類是泛型的這一事實。其返回類型為 Class 對象的類型參數: ``` public class Class<T> { T cast(Object obj); } ``` This is precisely what’s needed by the getFavorite method. It is what allows us to make Favorites typesafe without resorting to an unchecked cast to T. 這正是 getFavorite 方法所需要的。它使我們能夠使 Favorites 類型安全,而不需要對 T 進行 unchecked 的轉換。 There are two limitations to the Favorites class that are worth noting. First, a malicious client could easily corrupt the type safety of a Favorites instance, by using a Class object in its raw form. But the resulting client code would generate an unchecked warning when it was compiled. This is no different from a normal collection implementations such as HashSet and HashMap. You can easily put a String into a `HashSet<Integer>` by using the raw type HashSet (Item 26). That said, you can have runtime type safety if you’re willing to pay for it. The way to ensure that Favorites never violates its type invariant is to have the putFavorite method check that instance is actually an instance of the type represented by type, and we already know how to do this. Just use a dynamic cast: Favorites 類有兩個`值`得注意的限制。首先,惡意客戶端很容易通過使用原始形式的類對象破壞 Favorites 實例的類型安全。但是生成的客戶端代碼在編譯時將生成一個 unchecked 警告。這與普通的集合實現(如 HashSet 和 HashMap)沒有什么不同。通過使用原始類型 HashSet([Item-26](/Chapter-5/Chapter-5-Item-26-Do-not-use-raw-types.md)),可以輕松地將 String 類型放入 `HashSet<Integer>` 中。也就是說,如果你愿意付出代價的話,你可以擁有運行時類型安全。確保 Favorites 不會違反其類型不變量的方法是讓 putFavorite 方法檢查實例是否是 type 表示的類型的實例,我們已經知道如何做到這一點。只需使用動態轉換: ``` // Achieving runtime type safety with a dynamic cast public <T> void putFavorite(Class<T> type, T instance) { favorites.put(type, type.cast(instance)); } ``` There are collection wrappers in java.util.Collections that play the same trick. They are called checkedSet, checkedList, checkedMap, and so forth. Their static factories take a Class object (or two) in addition to a collection (or map). The static factories are generic methods, ensuring that the compile-time types of the Class object and the collection match. The wrappers add reification to the collections they wrap. For example, the wrapper throws a ClassCastException at runtime if someone tries to put a Coin into your `Collection<Stamp>`. These wrappers are useful for tracking down client code that adds an incorrectly typed element to a collection, in an application that mixes generic and raw types. java.util.Collections 中的集合包裝器也具有相同的功能。它們被稱為 checkedSet、checkedList、checkedMap,等等。除了集合(或 Map)外,它們的靜態工廠還接受一個(或兩個)Class 對象。靜態工廠是通用方法,確保 Class 對象和集合的編譯時類型匹配。包裝器將具體化添加到它們包裝的集合中。例如,如果有人試圖將 Coin 放入 `Collection<Stamp>` 中,包裝器將在運行時拋出 ClassCastException。在混合了泛型類型和原始類型的應用程序中,這些包裝器對跟蹤將類型錯誤的元素添加到集合中的客戶端代碼非常有用。 The second limitation of the Favorites class is that it cannot be used on a non-reifiable type (Item 28). In other words, you can store your favorite String or String[], but not your favorite `List<String>`. If you try to store your favorite `List<String>`, your program won’t compile. The reason is that you can’t get a Class object for `List<String>`. The class literal `List<String>.class` is a syntax error, and it’s a good thing, too. `List<String>` and `List<Integer>` share a single Class object, which is List.class. It would wreak havoc with the internals of a Favorites object if the “type literals” `List<String>.class` and `List<Integer>.class` were legal and returned the same object reference. There is no entirely satisfactory workaround for this limitation. Favorites 類的第二個限制是它不能用于不可具體化的類型([Item-28](/Chapter-5/Chapter-5-Item-28-Prefer-lists-to-arrays.md))。換句話說,你可以存儲的 Favorites 實例類型為 String 類型或 String[],但不能存儲 `List<String>`。原因是你不能為 `List<String>` 獲取 Class 對象,`List<String>.class` 是一個語法錯誤,這也是一件好事。`List<String>` 和 `List<Integer>` 共享一個 Class 對象,即 List.class。如果「字面類型」`List<String>.class` 和 `List<Integer>.class` 是合法的,并且返回相同的對象引用,那么它將嚴重破壞 Favorites 對象的內部結構。對于這個限制,沒有完全令人滿意的解決方案。 The type tokens used by Favorites are unbounded: getFavorite and put-Favorite accept any Class object. Sometimes you may need to limit the types that can be passed to a method. This can be achieved with a bounded type token, which is simply a type token that places a bound on what type can be represented, using a bounded type parameter (Item 30) or a bounded wildcard (Item 31). Favorites 使用的類型標記是無界的:getFavorite 和 put-Favorite 接受任何 Class 對象。有時你可能需要限制可以傳遞給方法的類型。這可以通過有界類型標記來實現,它只是一個類型標記,使用有界類型參數([Item-30](/Chapter-5/Chapter-5-Item-30-Favor-generic-methods.md))或有界通配符([Item-31](/Chapter-5/Chapter-5-Item-31-Use-bounded-wildcards-to-increase-API-flexibility.md))對可以表示的類型進行綁定。 The annotations API (Item 39) makes extensive use of bounded type tokens. For example, here is the method to read an annotation at runtime. This method comes from the AnnotatedElement interface, which is implemented by the reflective types that represent classes, methods, fields, and other program elements: annotation API([Item-39](/Chapter-6/Chapter-6-Item-39-Prefer-annotations-to-naming-patterns.md))廣泛使用了有界類型標記。例如,下面是在運行時讀取注釋的方法。這個方法來自 AnnotatedElement 接口,它是由表示類、方法、字段和其他程序元素的反射類型實現的: ``` public <T extends Annotation> T getAnnotation(Class<T> annotationType); ``` The argument, annotationType, is a bounded type token representing an annotation type. The method returns the element’s annotation of that type, if it has one, or null, if it doesn’t. In essence, an annotated element is a typesafe heterogeneous container whose keys are annotation types. 參數 annotationType 是表示注釋類型的有界類型標記。該方法返回該類型的元素注釋(如果有的話),或者返回 null(如果沒有的話)。本質上,帶注釋的元素是一個類型安全的異構容器,其`鍵`是注釋類型。 Suppose you have an object of type `Class<?>` and you want to pass it to a method that requires a bounded type token, such as getAnnotation. You could cast the object to `Class<? extends Annotation>`, but this cast is unchecked, so it would generate a compile-time warning (Item 27). Luckily, class Class provides an instance method that performs this sort of cast safely (and dynamically). The method is called asSubclass, and it casts the Class object on which it is called to represent a subclass of the class represented by its argument. If the cast succeeds, the method returns its argument; if it fails, it throws a ClassCastException. 假設你有一個 `Class<?>` 類型的對象,并且希望將其傳遞給一個需要有界類型令牌(例如 getAnnotation)的方法。你可以將對象強制轉換為 `Class<? extends Annotation>`,但是這個強制轉換是未選中的,因此它將生成一個編譯時警告([Item-27](/Chapter-5/Chapter-5-Item-27-Eliminate-unchecked-warnings.md))。幸運的是,class 類提供了一個實例方法,可以安全地(動態地)執行這種類型的強制轉換。該方法稱為 asSubclass,它將類對象強制轉換為它所調用的類對象,以表示由其參數表示的類的子類。如果轉換成功,則該方法返回其參數;如果失敗,則拋出 ClassCastException。 Here’s how you use the asSubclass method to read an annotation whose type is unknown at compile time. This method compiles without error or warning: 下面是如何使用 asSubclass 方法讀取在編譯時類型未知的注釋。這個方法編譯沒有錯誤或警告: ``` // Use of asSubclass to safely cast to a bounded type token static Annotation getAnnotation(AnnotatedElement element,String annotationTypeName) { Class<?> annotationType = null; // Unbounded type token try { annotationType = Class.forName(annotationTypeName); } catch (Exception ex) { throw new IllegalArgumentException(ex); } return element.getAnnotation(annotationType.asSubclass(Annotation.class)); } ``` In summary, the normal use of generics, exemplified by the collections APIs, restricts you to a fixed number of type parameters per container. You can get around this restriction by placing the type parameter on the key rather than the container. You can use Class objects as keys for such typesafe heterogeneous containers. A Class object used in this fashion is called a type token. You can also use a custom key type. For example, you could have a DatabaseRow type representing a database row (the container), and a generic type `Column<T>` as its key. 總之,以集合的 API 為例的泛型在正常使用時將每個容器的類型參數限制為固定數量。你可以通過將類型參數放置在`鍵`上而不是容器上來繞過這個限制。你可以使用 Class 對象作為此類類型安全異構容器的`鍵`。以這種方式使用的 Class 對象稱為類型標記。還可以使用自定義`鍵`類型。例如,可以使用 DatabaseRow 類型表示數據庫行(容器),并使用泛型類型 `Column<T>` 作為它的`鍵`。 --- **[Back to contents of the chapter(返回章節目錄)](/Chapter-5/Chapter-5-Introduction.md)** - **Previous Item(上一條目):[Item 32: Combine generics and varargs judiciously(明智地合用泛型和可變參數)](/Chapter-5/Chapter-5-Item-32-Combine-generics-and-varargs-judiciously.md)** - **Next Item(下一條目):[Chapter 6 Introduction(章節介紹)](/Chapter-6/Chapter-6-Introduction.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>

                              哎呀哎呀视频在线观看