Effective Java 3rd-----Chapter 5

Chapter 5 Generics

范型

SINCE Java 5, generics have been a part of the language. Before generics, you had to cast every object you read from a collection. If someone accidentally inserted an object of the wrong type, casts could fail at runtime. With generics, you tell the compiler what types of objects are permitted in each collection. The compiler inserts casts for you automatically and tells you at compile time if you try to insert an object of the wrong type. This results in programs that are both safer and clearer, but these benefits, which are not limited to collections, come at a price. This chapter tells you how to maximize the benefits and minimize the complications.

自从Java 5之后,范型成为了java语言的一部分。在范型之前,你必须对每一个从集合中读出的元素进行转型。如果一个人不小心向集合中插入了一个错误的类型,就会在转型时抛出一个运行时异常。通过范型,你在告诉编译器每个集合允许哪种类型的对象。编译器在插入时自动做类型转换并且在编译时候告诉你,你是否插入了一个错误的对象类型。编程时这样做相比于不限制集合类型更加安全也更加清晰,但是有一些难度。本章告诉你怎么样最大化收益,并且尽量降低难度。

Item 26: Don`t use raw types

Item 26: 不要使用原生态类型(区别于原生类型)

范型常见变量E,代表Element

a few terms
一些术语

A class or interface whose declaration has one or more type parameters is a generic class or interface
声明一个或多个类型参数(type parameters)的来或者接口成为范型类或范型接口

Generic classes and interfaces are collectively known as generic types.
范型类和接口统称为范型(generic types)

Each generic type defines a set of parameterized types, which consist of the class or interface name followed by an angle-bracketed list of actual type parameters corresponding to the generic type’s formal type parameters [JLS, 4.4, 4.5].
每一个范型定义一系列参数化类型(parameterized types),构成格式为:类或接口名称,然后是一个在尖括号内的范型形式参数的实际类型参数列表

Finally, each generic type defines a raw type, which is the name of the generic type used without any accompanying type parameters [JLS, 4.8].
最后,每个范型对应定义一个原生态类型,一个与去掉类型参数的范型。List对应List

As mentioned throughout this book, it pays to discover errors as soon as possible after they are made, ideally at compile time.
作为本书的典型思考方式,错误应该在发生之后尽快被发现,理想的是在编译的时候。(禁止使用原生态类型的理论依据)

While the prospect of accidentally inserting a coin into a stamp collection may appear far-fetched, the problem is real. For example, it is easy to imagine putting a BigInteger into a collection that is supposed to contain only BigDecimal instances.
虽然,将一个coin类插入到一个stamp的集合的意外有点扯,但是这类问题是存在的。比如,很容易将一个BigInteger插入到BigDecimal集合中。

As noted earlier, it is legal to use raw types (generic types without their type parameters), but you should never do it. If you use raw types, you lose all the safety and expressiveness benefits of generics.Given that you shouldn’t use them, why did the language designers permit raw types in the first place? For compatibility.
如上所述,使用原生态类型是合法的,但是永远不要使用。一旦你使用原生态类型,就失去了范型的安全性和可读性的收益。既然不应该使用原生态类型,为什么语言设计层面允许使用,是为了兼容性。

s a consequence, you lose type safety if you use a raw type such as List, but not if you use a parameterized type such as List.
如果使用原生态类型List,不使用参数化的List类型,失去了参数类系安全检查。(List可以被赋值为List,而List则不行。

If you want to use a generic type but you don’t know or care what the actual type parameter is, you can use a question mark instead. For example, the unbounded wildcard type for the generic type Set is Set (read “set of some type”). It is the most general parameterized Set type, capable of holding any set.
如果你想使用范型,但是你不知道或者不关心实际的类型参数是什么,你可以无限制的通配符类型。(unbounded wildcard types)这是最普通的参数化类型,可以持有任何集合。

What is the difference between the unbounded wildcard type Set and the raw type Set? Does the question mark really buy you anything? Not to belabor the point, but the wildcard type is safe and the raw type isn’t. You can put any element into a collection with a raw type, easily corrupting the collection’s type invariant (as demonstrated by the unsafeAdd method on page 119); you can’t put any element (other than null) into a Collection.

Set和Set的区别是什么?这个问号真的起作用了么?这一点不需要赘述,但是通配符类型是安全的但是原生态类型则不是,原生态类型可以put进任何类型的元素,但是除了null外你不能put进任何元素到通配符集合中。

There are a few minor exceptions to the rule that you should not use raw types. You must use raw types in class literals. The specification does not permit the use of parameterized types (though it does permit array types and primitive types) [JLS, 15.8.2]. In other words, List.class, String[].class, and int.class are all legal, but List.class and List.class are not.
对于不要使用原生态类型的规则有两个例外。你必须使用原生态类型在类的字面值。这些例外情况不允许使用参数化类型(虽然允许数组和原始类型),换句话说 List.class, String[].class, and int.class 都是合法的,但是List.class and List.class 是不合法的。

A second exception to the rule concerns the instanceof operator. Because generic type information is erased at runtime, it is illegal to use the instanceof operator on parameterized types other than unbounded wildcard types. The use of unbounded wildcard types in place of raw types does not affect the behavior of the instanceof operator in any way. In this case, the angle brackets and question marks are just noise. This is the preferred way to use the instanceof operator with generic types:

第二个特例是关于instanceof操作符。因为范型信息在运行时被擦除,因此在参数化类型而非无限制通配符类型上使用instanceof操作符是违法的。使用无限制通配符替代原生态类型不会影响instanceof操作符的行为。在这种情况下,就显得多余了,下面是使用instanceof操作符的推荐方法:

// Legitimate use of raw type - instanceof operator 
if (o instanceof Set) { // Raw type 
    Set s = (Set) o; // Wildcard type 
    ... 
} 

上述代码中if语句中的Set可以是Set但是由于类型擦除没有必要。

Item 27: Eliminate unchecked warnings

限制非检查警告

@SuppressWarnings("unchecked")注解可以限制警告,应该在尽量小的范围使用

Eliminate every unchecked warning that you can.
解决每一个非检查警告。

If you can’t eliminate a warning, but you can prove that the code that provoked the warning is typesafe, then (and only then) suppress the warning with an @SuppressWarnings("unchecked") annotation.
如果你不能限制一个警告,但是你能确定引起这个警告的代码是类型安全的。在只有在这种情况下,使用@SuppressWarnings("unchecked") 注解

Always use the SuppressWarnings annotation on the smallest scope possible.
应该在尽量小的范围使用@SuppressWarnings("unchecked")注解

Every time you use a @SuppressWarnings("unchecked") annotation, add a comment saying why it is safe to do so.
每次使用@SuppressWarnings("unchecked")注解,添加一段注释说明为什么这么做是安全的。

Summary

In summary, unchecked warnings are important. Don’t ignore them. Every unchecked warning represents the potential for a ClassCastException at runtime. Do your best to eliminate these warnings. If you can’t eliminate an unchecked warning and you can prove that the code that provoked it is typesafe, suppress the warning with a @SuppressWarnings("unchecked") annotation in the narrowest possible scope. Record the rationale for your decision to suppress the warning in a comment.
总的来说,非检查警告非常重要。不要忽视它们。每个非检查警告都代表着一个运行时类型转化类长的可能性。尽你最大的可能去限制这些警告。如果不能消除警告,同时可以证明引起警告的代码是类型安全的就可以在尽可能小的范围加上@SuppressWarnings("unchecked")注解。要用注释把禁止警告的原因记录下来。

Item 28: Prefer lists to arrays

列表优先于数组

Arrays differ from generic types in two important ways. First, arrays are covariant. This scary-sounding word means simply that if Sub is a subtype of Super, then the array type Sub[] is a subtype of the array type Super[]. Generics, by contrast, are invariant: for any two distinct types Type1 and Type2, List is neither a subtype nor a supertype of List You might think this means that generics are deficient, but arguably it is arrays that are deficient.
数组和范型有两个重要的不同点。首先,数组是协变的,这听起来很可怕,但是很简单它表示,如果Sub是Super的子类,则Sub[]是Supper[]的子类。相反范型是不变的。对于任意两种不同的类型Type1和Type2,List即不是List的子类也不是他的父类。你可能会想范型是有缺陷的,但是也可以说明数组才是有缺陷的。

// Fails at runtime!
Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
but this one is not: 
// Won't compile!
List ol = new ArrayList(); // Incompatible types 
ol.add("I don't fit in"); 
 
 

以上代码说明应该在尽可能早点的时候发现,并抛出异常。

The second major difference between arrays and generics is that arrays are reified [JLS, 4.7]. This means that arrays know and enforce their element type at runtime. As noted earlier, if you try to put a String into an array of Long, you’ll get an ArrayStoreException. Generics, by contrast, are implemented by erasure [JLS, 4.6]. This means that they enforce their type constraints only at compile time and discard (or erase) their element type information at runtime. Erasure is what allowed generic types to interoperate freely with legacy code that didn’t use generics (Item 26), ensuring a smooth transition to generics in Java 5.
第二个主要的区别是数组是具体话的。这表示数组在运行时才知道,并检查它们的元素类型约束。如上所述,如果你想Long数组中put一个String,你会得到一个ArrayStoreException异常。相比之下范型是通过类型擦除实现的。这表示范型强制要求它们的类型限制仅在编译期,并且在运行时擦除它们的元素类型信息。擦除就是范型可以与没有使用范型的代码相互作用。保证到Java5是一个平滑的过度。

Because of these fundamental differences, arrays and generics do not mix well. For example, it is illegal to create an array of a generic type, a parameterized type, or a type parameter. new List[], new List[], new E[]. All will result in generic array creation errors at compile time.
由于以上不同,数组和范型不能很好的结合使用。创建一个范型数组,参数化类型数组,或者一个类型参数数组都是非法的。new List[], new List[], new E[].都会在编译期产生一个范型数组创建错误。

Why is it illegal to create a generic array? Because it isn’t typesafe.
为什么创建创建范型数组是非法的,因为那不是类型安全的。(List[]如果合法,范型是通过类型擦除实现的,所以在编译期,会变成List[],这个时候赋值给List[]一个List元素是合法的,但是范型在取值时候会做强制类型转换,会抛出异常)

Types such as E, List, and List are technically known as < types [JLS, 4.7]. Intuitively speaking, a non-reifiable type is one whose runtime representation contains less information than its compile-time representation. Because of erasure, the only parameterized types that are reifiable are unbounded wildcard types such as List and Map (Item 26). It is legal, though rarely useful, to create arrays of unbounded wildcard types.
E, List, and List这些类型在技术上统称为不可具体化类型。通俗的说,不可具体化类型是一种在运行时包含的信息比编译期少的信息的类型。由于类型擦除,唯一具体话的参数化类型是无界通配符,例如List and Map。创建一个无界通配符的数组是合法的,但是不常见

It also means that you get confusing warnings when using varargs methods (Item 53) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters.

在创建可变参数方法和范型联合使用时,你会看到一些令人费解的警告。这是因为每次你创建一个可变参数方法,一个数组会创建用来存储参数。

Summary

In summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequence, arrays provide runtime type safety but not compile-time type safety, and vice versa for generics. As a rule, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time errors or warnings, your first impulse should be to replace the arrays with lists.
总的来说,数组和范型有不同的类型规则。数组是协变的具体话的,范型是不可变的和擦除的。因此数组提供了运行时的类型安全,但没有编译时的类型安全。范型则正好相反。作为一个规则,数组和范型不能很好的混合工作。如果混合使用它们,会得到编译期错误或者警告,最应该先想到的解决办法是用列表替换数组。

Item 29: Favor generic types

优先考虑范型

There are two reasonable ways to solve it.
有两个可以绕过范型数组非法的方法。

elements = new E[DEFAULT_INITIAL_CAPACITY]; // error: generic array creation
  • The first solution directly circumvents the prohibition on generic array creation: create an array of Object and cast it to the generic array type.
  • 第一个解决方法,声明Object数组之后强转。但是不是类型安全的。
elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; // warning: [unchecked] unchecked cast

The compiler may not be able to prove that your program is typesafe, but you can. You must convince yourself that the unchecked cast will not compromise the type safety of the program. The array in question (elements) is stored in a private field and never returned to the client or passed to any other method. The only elements stored in the array are those passed to the push method, which are of type E, so the unchecked cast can do no harm.
编译器不能保证你的程序是类型安全的,但是你可以。你必须说服自己,没检查的转换不会引起类型安全问题。以上问题中的数组是作为私有字段存储的,并且不会被return,或者被传递到其他任何方法中。这个数组中存储的唯一类型是通过push方法传进来的元素,而这个方法限制了参数类型是E,所以这个强制类型转换是不会造成伤害的。(整体上为了确认会不会加进来其他类型元素,通过跟踪变量的程序引用确认,可以通过@SuppressWarnings("unchecked"))

  • The second way to eliminate the generic array creation error in Stack is to change the type of the field elements from E[] to Object[].
  • 第二种方法是将elements的声明从E[]变为Object[]

Both techniques for eliminating the generic array creation have their adherents. The first is more readable: the array is declared to be of type E[], clearly indicating that it contains only E instances. It is also more concise: in a typical generic class, you read from the array at many points in the code; the first technique requires only a single cast (where the array is created), while the second requires a separate cast each time an array element is read. Thus, the first technique is preferable and more commonly used in practice. It does, however, cause heap pollution (Item 32): the runtime type of the array does not match its compile-time type (unless E happens to be Object). This makes some programmers sufficiently queasy that they opt for the second technique, though the heap pollution is harmless in this situation.
这两种方法有他们各自的拥护者。第一种更加易读,数组声明为E[],清晰,他们只包含E类型的实例。简洁,在一个典型的范型类,你可能会从多个地方读取数组的值,第一种技术只需要一个转换(数组创建的时候),然而第二种需要很多分开的类型转换在读取数组元素的时候。因此第一种方法更加受欢迎,在实践中使用的更加广泛。然而第一种方法会造成堆污染:数组的运行时类型和编译时类型不同。这使得很多开发者觉得不适应,所以他们选择第二种方式,虽然堆污染在这种情况下基本没有实质性的伤害。

Summary

In summary, generic types are safer and easier to use than types that require casts in client code. When you design new types, make sure that they can be used without such casts. This will often mean making the types generic. If you have any existing types that should be generic but aren’t, generify them. This will make life easier for new users of these types without breaking existing clients (Item 26).
总的来说,范型更加安全也更加易于使用相比于需要在客户端进行类型转换的。设计新的类型时,确保他们在使用时不需要类型转换。这通常代表着使类型变成范型。只要有时间,就把需要范型化的类型范型化。这将使生命周期更加清晰,对于新的使用者,并且不会坡缓现有客户端。

Favor generic methods

优先考虑范型方法

static  Set merge(Set set1, Set set2) {
    Set set = new HashSet();
    set.addAll(set1);
    set.addAll(set2);
    return set;
}

调用以上代码的时候只要两个参数都传入为相同的范型,则不需要指定,叫做类型推倒。

generic singleton factory

范型单例工厂

Code like :

private static UnaryFunction ID_FUNC = new UnaryFunction() {
    @Override
    public Object apply(Object arg) {
        return arg;
    }
};

@SuppressWarnings("unchecked")
public static  UnaryFunction idFunction() {
    return (UnaryFunction) ID_FUNC;
}

public static void main(String[] args) throws UnsupportedEncodingException {
    String[] strings = {"Peter", "Paul", "Mary"};
    UnaryFunction names = idFunction();
    for(String s : strings)
    {
        System.out.println(names.apply(s));
    }

    Number[] numbers = {1, 2.0, 3L};
    UnaryFunction nums = idFunction();
    for(Number n : numbers)
    {
        System.out.println(nums.apply(n));
    }
}

 
 

需要确认apply方法是不是会对对象进行类型转换。

recursive type bound
递归类型限制

public static > E max(Collection c); 

Summary

In summary, generic methods, like generic types, are safer and easier to use than methods requiring their clients to put explicit casts on input parameters and return values. Like types, you should make sure that your methods can be used without casts, which often means making them generic. And like types, you should generify existing methods whose use requires casts. This makes life easier for new users without breaking existing clients (Item 26).
总的来说,范型方法,像范型类型一样,相比于需要客户端自己进行入参或者返回值的强制类型转换,都是更安全,更容易使用的。就像类型一样,你需要确保你的方法可以不需要转型直接使用,这一般意义上代表着使用范型。并且像类型一样,还应该将现有方法范型化,使新的用户使用起来更佳容易,并且不会破坏现有客户端。

Item 31: Use bounded wildcards to increase API flexibility

使用有界通配符提高接口的灵活性

List 和 List区别是第一个可以多个元素不同,即多个元素是Number的不同子类。第二个是Number的相同子类,在编译期限制了元素必须相同。

public class Stack {
    public Stack();
    public void push(E e);
    public E pop();
    public boolean isEmpty();
}
// pushAll method without wildcard type - deficient!
public void pushAll(Iterable) {
    for (E e : src)
        push(e); 
}
Stack numberStack = new Stack<>();
Iterable integers = ... ;
numberStack.pushAll(integers);

StackTest.java:7: error: incompatible types: Iterable
cannot be converted to Iterable
        numberStack.pushAll(integers);

由于范型的不可变性,即Integer is subclass of Number, but Iterable not subclass Iterable.
将上述代码中Iterable修改为Iterable就可以了

For maximum flexibility, use wildcard types on input parameters that represent producers or consumers.
为了最大的灵活性,要在表示生产者或者消费者的输入参数上使用通配符类型。

PECS stands for producer-extends, consumer-super, known as Get and Put Principle
Do not use bounded wildcard types as return types.
不要使用有界通配符作为返回值类型

If the user of a class has to think about wildcard types, there is probably something wrong with its API.
如果一个类的用户必须思考通配符类型的内容,表示API可能有些问题。

public static  Set union(Set s1, Set s2)
Set integers = Set.of(1, 3, 5);
Set  doubles  = Set.of(2.0, 4.0, 6.0);
Set  numbers  = union(integers, doubles);
Set numbers = union(integers, doubles);

Prior to Java 8

there will be a long error message like this

Union.java:14: error: incompatible types
        Set numbers = union(integers, doubles);
                                       ^ 
  required: Set
  found:    Set
  where INT#1,INT#2 are intersection types:
    INT#1 extends Number,Comparable
    INT#2 extends Number,Comparable

If the compiler doesn’t infer the correct type, you can always tell it what type to use with an explicit type argument.

// Explicit type parameter - required prior to Java 8 
Set numbers = Union.union(integers, doubles); 

after Java 8

the type inference rules are more clever, but Integer And Double is implements Comparable. So must do like [Prior to Java 8]

if a type parameter appears only once in a method declaration, replace it with a wildcard.

public static void swap(List list, int i, int j) {
       swapHelper(list, i, j);
} 
   // Private helper method for wildcard capture
private static  void swapHelper(List list, int i, int j) {
    list.set(i, list.set(j, list.get(i))); 
} 

you can use like above code to avoid the rule, which List can add null only.

summary

In summary, using wildcard types in your APIs, while tricky, makes the APIs far more flexible. If you write a library that will be widely used, the proper use of wildcard types should be considered mandatory. Remember the basic rule: producer-extends, consumer-super (PECS). Also remember that all comparables and comparators are consumers.
总的来说,使用通配符类型在API比较复杂,但是可以API更加灵活。如果你编写一个即将被广泛应用的类库,一定要考虑广泛的使用通配符。记住一个基本原则:PECS。同时记住所有comparables and comparators都是消费者。

Item 32: Combine generics and varargs judiciously

谨慎将范型和可变参数一起使用

Varargs methods (Item 53) and generics were both added to the platform in Java 5.
可变参数方法和范型都是在java 5的时候添加的。

the SafeVarargs annotation constitutes a promise by the author of a method that it is typesafe.
java 7 之后出现了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注释一个方法。 那么需要做些什么才能确保这一点? 回想一下,调用方法时会创建一个通用数组,以保存可变参数。 如果方法没有在数组中存储任何东西(它会覆盖参数)并且不允许对数组的引用进行转义(这会使不受信任的代码访问数组),那么它是安全的。 换句话说,如果varargs参数数组仅用于从调用者向方法传递可变数量的参数,毕竟这是可变参数的目的,那么该方法是安全的。

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.
在java 8中,@SafeVarargs注释只可以在静态方法,final实例方法中使用,在java 9中,也可以使用在私有的实例方法中。(以上修改,总体上为了限制使用在可重写方法上)

Effective Java 3rd-----Chapter 5_第1张图片
effective_java_3_chapter_5_1.png

上图为反编译代码,由于类型擦除,动态参数传递的是Object的数组,所以返回的是Object的数组,所以会出现转型失败异常

Summary

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注解标注方法,来避免不愉快的方法使用。

Item 33: Consider typesafe heterogeneous containers

优先考虑类型安全的异构容器

异构容器代码如下:

// Typesafe heterogeneous container pattern - implementation
public class Favorites {
    private Map, Object> favorites = new HashMap<>();
    public  void putFavorite(Class type, T instance) {
        favorites.put(Objects.requireNonNull(type), instance);
    } 
    public  T getFavorite(Class type) { 
        return type.cast(favorites.get(type)); 
    } 
} 

异构容器通过Class实现

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的printf方法和C的不同,应该用%n代替\n,\n在大部分平台好用,但不是所有平台。

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, the next Class, and so on. That’s where the heterogeneity comes from.
需要注意的是无界通配符是嵌套的:无界通配符不是map的key的类型。无界通配符表示,每个key可以是不同的参数化类型,一个可能是Class,下一个可能是Class,依此类推。这就是异构名称的由来。

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. 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:
有两个点需要注意。

  • 一个恶意的客户端可以轻易的打破类型安全,通过使用一个类型的原生态类型。改进的方法是通过在put方法中加入类型校验。
public  void putFavorite(Class type, T instance) {
    favorites.put(Objects.requireNonNull(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.
这有一些集合的包装器在java.util.Collections中扮演着参数类型校验的角色。他们是 checkedSet, checkedList, checkedMap等。

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, String[], 但是不可以存储List

Summary

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 as its key.
总而言之,集合API说明了范型的一般用法,限制你每个容器只能有固定数目的类型参数。你可以通过将类型参数放在key上而不是容器上来避开这一限制,对于这种类型安全的异构容器,可以用Class对象作为键值。以这种方式使用class对象作为键的方式叫做类型令牌。也可以使用定制的键类型。例如DatabaseRow type 可以使用类型Column作为key

你可能感兴趣的:(Effective Java 3rd-----Chapter 5)