自己翻译的Java.In.A.Nutshell.5th中泛型一章,欢迎拍砖把文章砸的漂亮一些

[size=14]做些说明吧:转网页的东西,改起来稍觉得的麻烦,就可能滞后。不过pdf文件会不定时的及时更新[/size]

4.1. Generic Types
Generic types and methods are the defining new feature of Java 5.0. A generic type is defined using one or more type variables and has one or more methods that use a type variable as a placeholder for an argument or return type. For example, the type java.util.List<E> is a generic type: a list that holds elements of some type represented by the placeholder E. This type has a method named add(), declared to take an argument of type E, and a method named get(), declared to return a value of type E.

4.1. 泛型

泛类型和泛型方法是Java5.0中的新特性。一种泛类型用一个或多个泛型变量定义,可以有一个或多个,泛型变量做参数占位符或做返回值的方法。例如,类型 java.util.List<E> 是一种泛类型:一个 list ,它的元素类型是E 这个占位符表示的类型。这个类型有一个叫 add() 的方法,有一个参数类型为 E ,有一个名叫 get() 的方法,返回一个类型为 E 的值。

In order to use a generic type like this, you specify actual types for the type variable (or variables), producing a parameterized type such as List<String>.[1] The reason to specify this extra type information is that the compiler can provide much stronger compile-time type checking for you, increasing the type safety of your programs. This type checking prevents you from adding a String[], for example, to a List that is intended to hold only String objects. Also, the additional type information enables the compiler to do some casting for you. The compiler knows that the get( ) method of a List<String> (for example) returns a String object: you are no longer required to cast a return value of type Object to a String.

为了可以象往常一样使用泛类型,你需要为泛型变量(或是变量)指定一个实际的类型,产生一个参数化类型,就象 List<String> 。这样做的原因是,为编译器提供一个特定的类型信息,让它可以在编译期为你做类型检查,这样可以大大增加你程序的类型安全。例如,有一个 List 打算容纳 String 类型的对象,这种类型安全检查阻止你增加一个String[] 的元素。而且,这附加的类型信息使编译器帮你做些造型的活儿。例如,编译器知道 List<String> 的 get() 方法返回一个String类型对象,你不再需要把Object类型的返回值造型成一个String类型的对象。

[1] Throughout this chapter, I've tried to consistently use the term " generic type" to mean a type that declares one or more type variables and the term "parameterized type" to mean a generic type that has had actual type arguments substituted for its type varaiables. In common usage, however, the distinction is not a sharp one and the terms are sometimes used interchangeably.

[1] 整个这章,我们将统一使用这些术语。"泛类型":意味着一个类型,可以声明一个或多个泛型变量。"参数化类型",意味着一个(运行期的)泛类型,表示它的泛型变量被实际类型做为参数值替换了。但在通常的应用中,两个术语的差别不是太明显,有时还可以替换。

The collections classes of the java.util package have been made generic in Java 5.0, and you will probably use them frequently in your programs. Typesafe collections are the canonical use case for generic types. Even if you never define generic types of your own and never use generic types other than the collections classes in java.util, the benefits of typesafe collections are so significant that they justify the complexity of this major new language feature.

在Java 5.0中,包java.util中的Collection类都已经被泛化了,你可能会在程序中频繁的用到它们。类型安全的Collection是应用泛类型的典范。可能你还从没有定义过自己的泛类型,也从没用过Collection(在包 java.uitl中)之外的泛类型,但类型安全的Collection的好处是显而易见的,它将证实这个新的,复杂的,主要且重要的语言特性。

We begin by exploring the basic use of generics in typesafe collections, then delve into more complex details about the use of generic types. Next we cover type parameter wildcards and bounded wildcards. After describing how to use generic types, we explain how to write your own generic types and generic methods. Our coverage of generics concludes with a tour of important generic types in the core Java API. It explores these types and their use in depth in order to provide a deeper understanding of how generics work.

我们将从探索类型安全的Collection的基本泛化用法开始,然后深入泛类型用法更为复杂的细节。接下来,我们将覆盖泛型参数通配符和边界通配符。再说明怎么用泛类型,我们将阐述怎样写自己的泛类型和泛型方法。我们涉及的泛型知识包括了 Java API 中关于泛类型的主要部分。深入的探讨了这些类型和他们的用法,为的是更深入的理解泛型是如何工作的。

4.1.1. Typesafe Collections
4.1.1.类型安全的Collection

The java.util package includes the Java Collections Framework for working with sets and lists of objects and mappings from key objects to value objects. Collections are covered in Chapter 5. Here, we discuss the fact that in Java 5.0 the collections classes use type parameters to identify the type of the objects in the collection. This is not the case in Java 1.4 and earlier. Without generics, the use of collections requires the programmer to remember the proper element type for each collection. When you create a collection in Java 1.4, you know what type of objects you intend to store in that collection, but the compiler cannot know this. You must be careful to add elements of the appropriate type. And when querying elements from a collection, you must write explicit casts to convert them from Object to their actual type. Consider the following Java 1.4 code:

在包 java.util 中包含了 Java 的Collection框架(set、list--关于对象、map--关于关键字对象和值对象的对)。 Collection 将在第五章讨论。这里我们只讨论有关在 Java 5.0 的Collection类中,用泛型参数标识Collection对象类型的内容。这在 Java 1.4 或是更早的 Java 版本中,没有这个用法。在没有泛型应用Collection的时候,需要程序员正确记得每个Collection的每个元素的类型。当我们建立一个 Java 1.4 的Collection时,你知道打算把什么样的类型对象放入这个Collection中,但是编译器不知道这些。你增加元素时必须小心正确性。并且当获取一个Collection的元素时,你必须显示的造型对象Object 到它们实际的类型。思考在 Java 1.4 中的如下代码:

public static void main(String[] args) {
// This list is intended to hold only strings.
// 这个list仅容纳字符串对象。
// The compiler doesn't know that so we have to remember ourselves.
// 编译器不知道这些,我们必须自己记住。
List wordlist = new ArrayList();

// Oops! We added a String[] instead of a String.
// 哎哟! 我们增加了个String[]元素,而不是String类型的。
// The compiler doesn't know that this is an error.
// 编译器是不知道那个错误的
wordlist.add(args);

// Since List can hold arbitrary objects, the get() method returns
// Object. Since the list is intended to hold strings, we cast the
// return value to String but get a ClassCastException because of
// the error above.
// 因为 List 能容纳任意的对象,方法仅返回Object的对象。因为这个 list 打算
// 存储字符串的,我们把返回值转换成字符串,但却因为上面的错误得到了一个
// ClassCastException的异常。

String word = (String)wordlist.get(0);
}


Generic types solve the type safety problem illustrated by this code. List and the other collection classes in java.util have been rewritten to be generic. As mentioned above, List has been redefined in terms of a type variable named E that represents the type of the elements of the list. The add( ) method is redefined to expect an argument of type E instead of Object and get( ) has been redefined to return E instead of Object.

泛类型就是解决上面示例的安全性问题的。在包java.utiil中的List和其他的Collection类都应用泛型重写过了。在上面说到的,List已被应用泛型变量 E (表示 list 中元素的类型) 重新定义过了, add() 方法被重定义成,要求传递一个类型为E而不是Object的参数;get()方法被重定义成返回一个类型为E而不是 Object 的值。

In Java 5.0, when we declare a List variable or create an instance of an ArrayList, we specify the actual type we want E to represent by placing the actual type in angle brackets following the name of the generic type. A List that holds strings is a List<String>, for example. Note that this is much like passing an argument to a method, except that we use types rather than values and angle brackets instead of parentheses.

在 Java 5.0 ,当我们声明一个List变量或是创建一个ArrayList的实例时,我们指定一个实际少泛型替换类型占位符E,这个实际类型被放到泛类型名后的尖括号内。例如,一个容纳字符串类型的List就这样写List<String>。注意,这个很象一个有合法参数的方法,除了我们用类型替换了值,用尖括号替换了圆括号。

The elements of the java.util collection classes must be objects; they cannot be used with primitive values. The introduction of generics does not change this. Generics do not work with primitives: we can't declare a Set<char>, or a List<int> for example. Note, however, that the autoboxing and autounboxing features of Java 5.0 make working with a Set<Character> or a List<Integer> just as easy as working directly with char and int values. (See Chapter 2 for details on autoboxing and autounboxing).

包java.util中的类的元素必须是对象;它们不能用原生类型。引进泛型并不能改变这一点。泛型应用不是针对原生类型的。例如,我们不能声明Set<char>或是List<int>。但是,注意了,由于Java 5.0自动装箱和自动拆箱的新特性,我们可以应用Set<Character>或是List<Integer>,而直接使用char和int的值(参见第二章关于自动装箱和自动拆箱的详细讲述)。

In Java 5.0, the example above would be rewritten as follows:

在Java 5.0,上面的例子可能被重写成如下形式:

public static void main(String[] args) {
// This list can only hold String objects
// 这个 list 仅能容纳字符串对象
List<String> wordlist = new ArrayList<String>();

// args is a String[], not String, so the compiler won't let us do this
// args 是 String [] 类型的,不是String,因此编译器不让那么做。
wordlist.add(args); // Compilation error! // 编译错误!

// We can do this, though.
// Notice the use of the new for/in looping statement
// 然后,我们可以这么做
// 注意这个新的for/in的用法
for(String arg : args) wordlist.add(arg);

// No cast is required. List<String>.get() returns a String.
// 不需要造型转换。 List<String>.get() 返回一个字符串。
String word = wordlist.get(0);
}


Note that this code isn't much shorter than the nongeneric example it replaces. The cast, which uses the word String in parentheses, is replaced with the type parameter, which places the word String in angle brackets. The difference is that the type parameter has to be declared only once, but the list can be used any number of times without a cast. This would be more apparent in a longer example. But even in cases where the generic syntax is more verbose than the nongeneric syntax, it is still very much worth using generics because the extra type information allows the compiler to perform much stronger error checking on your code. Errors that would only be apparent at runtime can now be detected at compile time. Furthermore, the compilation error appears at the exact line where the type safety violation occurs. Without generics, a ClassCastException can be thrown far from the actual source of the error.

可以看到这段代码并不比非泛型的短多少。圆号形式的造型转换被尖括号的泛型参数替代了。不同的是,泛型参数只声明了一次,而list可以用很多次,且不用造型转换。在比较长的代码中这是显而易见的。但是,泛型语法比非泛型语法要繁琐很多,可是这是非常值得的,因为附加的类型信息允许编译器对你的代码执行强类型错误检查。这样运行期的错误可以在编译时就被找到。特别是,编译期里违反类型安全错误可以精确到行。若无泛型,一个ClassCastException的异常将会在源代码中很远的位置抛出。

Just as methods can have any number of arguments, classes can have more than one type variable. The java.util.Map interface is an example. A Map is a mapping from key objects to value objects. The Map interface declares one type variable to represent the type of the keys and one variable to represent the type of the values. As an example, suppose you want to map from String objects to Integer objects:

正如方法可以很多的参数一样,类可以有不止一种泛型变量。java.util.Map接口就是一个例子。一个Map是一个关键字对象和值对象的对。Map接口声明了一个表示关键字对象类型的泛型变量和一个表示值对象类型的泛型变量。例如,我们假定你有一个关键字类型为String,值类型为Integer的map:

public static void main(String[] args) {
// A map from strings to their position in the args[] array
// 一个关键字为字符串类型,表示args[] 数组位置的map
Map<String,Integer> map = new HashMap<String,Integer>();

// Note that we use autoboxing to wrap i in an Integer object.
// 注意了,我们用自动装箱把i放进Integer的对象中。
for(int i=0; i < args.length; i++) map.put(args[i], i);

// Find the array index of a word. Note no cast is required!
// 查找数组中的一个单词。注意,不需要造型!
Integer position = map.get("hello");

// We can also rely on autounboxing to convert directly to an int,
// but this throws a NullPointerException if the key does not exist
// in the map
// 但是这个关键字在 map 中不存在,会抛出一个NullPointerException异常
// 我也可以直接依靠自动拆箱把对象转化成int的值。

int pos = map.get("world");
}


A parameterized type like List<String> is itself a type and can be used as the value of a type parameter for some other type. You might see code like this:

一个象List<String>的 参数化类型是它自己的一个类型,可以作为值用作其他类型的参数化类型。你可以看下面的代码:

// Look at all those nested angle brackets!
// 注意看这些嵌套的尖括号!
Map<String, List<List<int[]>>> map = getWeirdMap();

// The compiler knows all the types and we can write expressions
// 编译器我们写的表达式的所有类型
// like this without casting. We might still get NullPointerException
// 象这种没有造型转换的。当然,我们仍然可能在运行时,得到 NullPointerException 异常
// or ArrayIndexOutOfBounds at runtime, of course.
// 或是 ArrayIndexOutOfBounds 异常
int value = map.get(key).get(0).get(0)[0];

// Here's how we break that expression down step by step.
// 在这我们怎么样一步步断句。
List<List<int[]>> listOfLists = map.get(key);
List<int[]> listOfIntArrays = listOfLists.get(0);
int[] array = listOfIntArrays.get(0);
int element = array[0];


In the code above, the get( ) methods of java.util.List<E> and java.util.Map<K,V> return a list or map element of type E and V respectively. Note, however, that generic types can use their variables in more sophisticated ways. Look up List<E> in the reference section of this book, and you'll find that its iterator( ) method is declared to return an Iterator<E>. That is, the method returns an instance of a parameterized type whose actual type parameter is the same as the actual type parameter of the list. To illustrate this concretely, here is a way to obtain the first element of a List<String> without calling get(0).

在上面的代码里, java.util.List<E> 和 java.util.Map<K,V> 的 get() 方法返回一个 list 或是类型分别为 E 和 V 的 map 。但是注意,这些泛类型能用更高级的方式表示他们的变量。参见本书有关 List<E> 的参考说明,你将发现它的 iterator() 方法被声明成返回一个 Iterator<E> 类型。那就是,方法返回一个参数化类型的实例,它的实际泛型参数和 list 的实际泛型参数相同。为了具体证明这一点,这有一个方法可以得到 List<String> 的第一个元素而不用调用 get(0) 方法

List<String> words = // ...initialized elsewhere... ...其他什么地方初始化...
Iterator<String> iterator = words.iterator();
String firstword = iterator.next();


4.1.2. Understanding Generic Types
4.1.2.理解泛类型
This section delves deeper into the details of generic type usage, explaining the following topics:

· The consequences of using generic types without type parameters

· The parameterized type hierarchy

· A hole in the compile-time type safety of generic types and a patch to ensure runtime type safety

· Why arrays of parameterized types are not typesafe

这部分将深入 generic type 的详细用法,简述以下主题:

应用没有泛型参数的泛类型的结果
参数化类型的层次
一个编译期的泛类型漏洞和一个保证运行期类型安全的补救办法
为什么数组的参数化类型是不安全的
4.1.2.1 Raw types and unchecked warnings
4.1.2.1原始类型和未检查的警告

Even though the Java collection classes have been modified to take advantage of generics, you are not required to specify type parameters to use them. A generic type used without type parameters is known as a raw type. Existing pre-5.0 code continues to work: you simply write all the casts that you're already used to writing, and you put up with some pestering from the compiler. Consider the following code that stores objects of mixed types into a raw List:

尽管 Java Collection类引入了泛型的优点,但是你不需要在用它们的时候指明泛型参数。没有泛型参数的泛类型将作为原始类型。现有的 5.0 以前的代码可以继续工作:你可以象以前一样简单的写所有的造型转换,然后忍受一些编译器的纠缠。思考下面用原始 List 存储混合类型对象的代码:

List l = new ArrayList();
l.add("hello");
l.add(new Integer(123));
Object o = l.get(0);


This code works fine in Java 1.4. If we compile it using Java 5.0, however, javac compiles the code but prints this complaint:

在Java 1.4下,这些代码会工作的很好。但如果用 Java 5.0 编译, javac 编译代码但会列出这个抱怨:

Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
注意:Test.java 使用了一个未检查或是不安全的操作。
注意:重新用 -Xlint:unchecked 参数编译查看详细信息。


When we recompile with the -Xlint option as suggested, we see these warnings:

当我们用建议的 -Xlint 选项编译后,看到了这样的警告:

Test.java:6: warning: [unchecked]
unchecked call to add(E) as a member of the raw type java.util.List
l.add("hello");
^
Test.java:7: warning: [unchecked]
unchecked call to add(E) as a member of the raw type java.util.List
l.add(new Integer(123));
^


The compiler warns us about the add( ) calls because it cannot ensure that the values being added to the list have the correct types. It is letting us know that because we've used a raw type, it cannot verify that our code is typesafe. Note that the call to get( ) is okay because it is extracting an element that is already safely in the list.

编译器给了我们关于调用 add() 方法的警告,因为它不能确定被增加到 list 中的值有正确的类型。这让我们知道我们用的是一个原始类型,它不能验证我们的代码是安全的。注意, get() 方法的调用是对的,因为它正在析取的元素已经安全的存储在 list 中了。

If you get unchecked warnings on files that do not use any of the new Java 5.0 features, you can simply compile them with the -source 1.4 flag, and the compiler won't complain. If you can't do that, you can ignore the warnings, suppress them with an @SuppressWarnings("unchecked") annotation (see Section 4.3 later in this chapter) or upgrade your code to specify a type parameter.[2] The following code, for example, compiles with no warnings and still allows you to add objects of mixed types to the list:

如果你的文件因为没有用到 Java 5.0 的新特性,而得到 unchecked 警告;你可以简单用的加 -source 1.4 参数编译,这样编译器将不再抱怨。如果你不那样做,你可以忽略这些警告,也可用 annotation (参见本章稍后的4.3部分)中的 @SuppressWarnings("uncheckd") 压制这些警告,或者,用泛型参数更新你的代码。例如下面的代码,当有你增加混合对象到 list 时,编译器将不再发出警告:

[2] At the time of this writing, javac does not yet honor the @SuppressWarnings annotation. It is expected to do so in Java 5.1.

[2] 就在本书写作时 ,javac 还不能兑现 annotation 的@SuppressWarnings操作。它将可以在Java 5.1版本中实现。

List<Object> l = new ArrayList<Object>();
l.add("hello");
l.add(123); // autoboxing 自动装箱
Object o = l.get(0);


4.1.2.2 The generic type hierarchy
parameterized types form a type hierarchy, just as normal types do. The hierarchy is based on the base type, however, and not on the type of the parameters. Here are some experiments you can try:

4.1.2.2 参数化类型的层次结构

参数化类型形成了一个典型的层次结构,就象通常类型一样的。但是这种分层是基于泛型参数的类型的,而不是基于参数化类型变量的类型的。这里有些试验你可以试一下

ArrayList<Integer> l = new ArrayList<Integer>();
List<Integer> m = l; // okay
Collection<Integer> n = l; // okay
ArrayList<Number> o = l; // error
Collection<Object> p = (Collection<Object>)l; // error, even with cast
// 错误,发生在造型那里


A List<Integer> is a Collection<Integer>, but it is not a List<Object>. This is nonintuitive, and it is important to understand why generics work this way. Consider this code:

一个 List<integer> 是一个 Collection<Ineger> ,但不是 List<Object> 。这个不太直观,但对于理解泛型是如何工作又是非常重要的。思考下面的代码

List<Integer> li = new ArrayList<Integer>();
li.add(123);

// The line below will not compile. But for the purposes of this
// thought-experiment, assume that it does compile and see how much
// trouble we get ourselves into.
// 下面的这行是不能编译的。但是依照我们通常的经验和观点,假定它能编译,看看
// 会有什么样的麻烦
List<Object> lo = li;

// Now we can retrieve elements of the list as Object instead of Integer
// 现在我用 Object 检索 list 中的元素,而不是用Integer
Object number = lo.get(0);

// But what about this?
// 但这是什么?
lo.add("hello world");

// If the line above is allowed then the line below throws ClassCastException
// 如果上面这行是对的,那么下面一行会抛出 ClassCastException 异常
Integer i = li.get(1); // Can't cast a String to Integer!



This then is the reason that a List<Integer> is not a List<Object>, even though all elements of a List<Integer> are in fact instances of Object. If the conversion to List<Object> were allowed, non-Integer objects could be added to the list.

这就是为什么 List<Integer> 不是 List<Object> ,虽说所有的 List<Integer> 元素是 Object 的实例。如果这种转化成 List<Object> 的操作是允许的,那么 non-Integer 的对象就会被允许加入到 list 中。

4.1.2.3 Runtime type safety
As we've seen, a List<X> cannot be converted to a List<Y>, even when X can be converted to Y. A List<X> can be converted to a List, however, so that you can pass it to a legacy method that expects an argument of that type and has not been updated for generics.

4.1.2.3 运行期安全

正如我们看到的, List<X> 不能被转化成 List<Y> ,尽管 X 能被转化成 Y 。但是 List<X> 能被转化成 List ,因此你可以通过这个方式处理遗留的代码(有那种泛型参数不能升级为泛型的代码)

This ability to convert parameterized types to nonparameterized types is essential for backward compatibility, but it does open up a hole in the type safety system that generics offer:

这种转化参数化类型为非参数化类型的能力是最基本的向后兼容,但这也给具有泛型的系统带来了一个类型安全漏洞。

// Here's a basic parameterized list.
// 这是一个基本的参数化类型类型的 list 。
List<Integer> li = new ArrayList<Integer>();

// It is legal to assign a parameterized type to a nonparameterized variable
// 这样把一个参数化类型赋给一个非参数化类型变量是合法的。
List l = li;

// This line is a bug, but it compiles and runs.
// The Java 5.0 compiler will issue an unchecked warning about it.
// If it appeared as part of a legacy class compiled with Java 1.4, however,
// then we'd never even get the warning.
// 这行是个 bug ,但是可以编译运行。
// Java 5.0 会发布一个 unchecked 警告信息
// 但是,如果用 Java 1.4 编译遗留的代码,不会有任何的警告。
l.add("hello");

// This line compiles without warning but throws ClassCastException at runtime.
// Note that the failure can occur far away from the actual bug.
// 这行编译没问题,但是会在运行时抛出 ClassCastException 异常
// 注意这个错误发现的地方,可能会远离它发生的实际位置
Integer i = li.get(0);


Generics provide compile-time type safety only. If you compile all your code with the Java 5.0 compiler and do not get any unchecked warnings, these compile-time checks are enough to ensure that your code is also typesafe at runtime. But if you have unchecked warnings or are working with legacy code that manipulates your collections as raw types, you may want to take additional steps to ensure type safety at runtime. You can do this with methods like checkedList() and checkedMap( ) of java.util.Collections. These methods enclose your collection in a wrapper collection that performs runtime type checks to ensure that only values of the correct type are added to the collection. For example, we could prevent the type safety hole shown above like this:

泛型仅提供了编译期的类型安全检查。如果你用 Java 5.0 编译代码并且没有任何的 unchecked 警告,那么这些编译的检查能够保证你的代码也在运行期是安全的。但是如果你收到 unchecked 警告,或是用了遗留下来的代码(用原始的类型操作Collection),你可能想用些附加的步骤来确保运行期的类型安全。你可以用 java.util.Collections 的这些方法 checkedList() 和 checkedMap() 来做。这些方法封装你的Collection到一个包装的Collection中,它执行运行期的检查确保正确类型的值被加入到你的Collection中。例如,我们可这样阻止类型安全的漏洞:

// Here's a basic parameterized list.
// 这是一个基本的参数化类型的 list 。
List<Integer> li = new ArrayList<Integer>();

// Wrap it for runtime type safety
// 为了运行期的安全,我们包装了它
List<Integer> cli = Collections.checkedList(li, Integer.class);

// Now widen the checked list to the raw type
// 现在放到这个 checked 的 list 到原始类型
List l = cli;

// This line compiles but fails at runtime with a ClassCastException.
// 这行可编译,但是运行期会抛出 ClassCastException 异常。
// The exception occurs exactly where the bug is, rather than far away
// 这个异常能够定位 bug 的位置,而不是远离它实际的位置
l.add("hello");


4.1.2.4 Arrays of generic type
4.1.2.4 关于数组的参数化类型

Arrays require special consideration when working with generic types. Recall that an array of type S[ ] is also of type T[], if T is a superclass (or interface) of S. Because of this, the Java interpreter must perform a runtime check every time you store an object in an array to ensure that the runtime type of the object and of the array are compatible. For example, the following code fails this runtime check and throws an ArrayStoreException:

当数组遇到泛类型时,我们需要特别的考虑。回想一个类型 S[] 的数组也是类型为 T[] 的数组,如果 T 是 S 的超类(或是接口)。因此,必须每当你存储一个对象到一个数组时,Java 的解析器就会执行一个运行期检查,以确保运行时放入的对象的类型和数组的类型相容。例如,下面的代码在运行期检查是失败的,并且抛出一个 ArrayStoreException 异常:

String[] words = new String[10];
Object[] objs = words;
objs[0] = 1; // 1 autoboxed to an Integer, throws ArrayStoreException
// 1 被自动装箱到 Integer,抛出ArrayStoreException 异常


Although the compile-time type of objs is Object[], its runtime type is String[ ], and it is not legal to store an Integer in it.

虽然编译时 objs 的类型是 Object[],但在运行时的类型是 String[] ,并非法的把 Integer 值放入其中。

When we work with generic types, the runtime check for array store exceptions is no longer sufficient because a check performed at runtime does not have access to the compile-time type parameter information. Consider this (hypothetical) code:

当我们应用泛类型时,运行期关于数组存储异常的检测已经不能满足需要,因为一个运行期的检测不能读取编译期的泛型参数信息。考虑下面的代码(假设可以执行):

List<String>[] wordlists = new ArrayList<String>[10];
ArrayList<Integer> ali = new ArrayList<Integer>();
ali.add(123);
Object[] objs = wordlists;
objs[0] = ali; // No ArrayStoreException //没有 ArrayStoreException 异常
String s = wordlists[0].get(0); // ClassCastException! //ClassCastException 异常


If the code above were allowed, the runtime array store check would succeed: without compile-time type parameters, the code simply stores an ArrayList into an ArrayList[] array, which is perfectly legal. Since the compiler can't prevent you from defeating type safety in this way, it instead prevents you from creating any array of generic type. The scenario above can never occur because the compiler will refuse to compile the first line.

如果上面的代码可以执行,运行时数组存储检查将是成功的;没有编译时的泛型参数,这段代码简单的把一个 ArrayList 存到 ArrayList[] 数组中,那都是非常正确的。因为编译器不能从这个方式阻止你破坏类型安全,它只能阻止你创建任何的参数化类型类型数组。其实上面的一幕是不会发生的,因为编译器将会拒绝第一个行的编译。

Note that this is not a blanket restriction on using arrays with generics; it is just a restriction on creating arrays of parameterized type. We'll return to this issue when we look at how to write generic methods.

注意这不是完全禁止数组应用泛型;这仅是禁止用参数化类型创建数组。当看过怎样写泛型方法后,会回到这个话题。

4.1.3. Type Parameter Wildcards
4.1.3. 泛型参数通配符

Suppose we want to write a method to display the elements of a List.[3] Before List was a generic type, we'd just write code like this:

假设我们想写一个显示 List 元素的方法。[3]在 List 是应用泛类型前,我们仅需要这样写代码:

[3] The three printList() methods shown in this section ignore the fact that the List implementations classes in java.util all provide working toString() methods. Notice also that the methods assume that the List implements RandomAccess and provides very poor performance on LinkedList instances.

[3] 这段的三个 priintList() 方法忽略了一个前提,就是 List 在 java.util 包中的实现类都实现了 toString() 方法。注意这些方法也假定 List 实现了 RandomAccess 功能和提供了比较弱的 LinkedList 实例的性能。

public static void printList(PrintWriter out, List list) {
for(int i=0, n=list.size(); i < n; i++) {
if (i > 0) out.print(", ");
out.print(list.get(i).toString());
}
}


In Java 5.0, List is a generic type, and, if we try to compile this method, we'll get unchecked warnings. In order to get rid of those warnings, you might be tempted to modify the method as follows:

在 Java 5.0 中, List 是一个泛类型,并且,如果你试着编译这个方法,将会得到 unchecked 警告。为了除去这些警告,你可能很想修改方法成下面的样子:

public static void printList(PrintWriter out, List<Object> list) {
for(int i=0, n=list.size(); i < n; i++) {
if (i > 0) out.print(", ");
out.print(list.get(i).toString());
}
}


This code compiles without warnings but isn't very useful because the only lists that can be passed to it are lists explicitly declared of type List<Object>. Remember that List<String> and List<Integer> (for example) cannot be widened or cast to List<Object>. What we really want is a typesafe printList() method to which we can pass any List, regardless of how it has been parameterized. The solution is to use a wildcard as the type parameter. The method would then be written like this:

这代码这么编译不会有警告,但没太大用处。因为仅当传递的是 List<Object> 类型的值给 list 时可用。还记得那个 List<String> 和 List<integer> 的例子吧,不可以放大造型成 List<Object>。怎么样才可以构建一个可以传任何类型的 List 的printList() 方法呢,而不关心这个 List 是否被泛化。解决的方法就是用泛型参数通配符。这个方法可以这么写:

public static void printList(PrintWriter out, List<?> list) {
for(int i=0, n=list.size(); i < n; i++) {
if (i > 0) out.print(", ");
Object o = list.get(i);
out.print(o.toString());
}
}


This version of the method compiles without warnings and can be used the way we want it to be used. The ? wildcard represents an unknown type, and the type List<?> is read as "List of unknown."

这个版本的方法可以顺利编译没有警告,并且做你想做的用途。这个"?"通配符表示不知道具体类型,这个类型 List<?> 读作"List of unknown"。

As a general rule, if a type is generic and you don't know or don't care about the value of the type variable, you should always use a ? wildcard instead of using a raw type. Raw types are allowed only for backward compatibility and should be used only in legacy code. Note, however, that you cannot use a wildcard when invoking a constructor. The following code is not legal:

作为泛化规则,如果一个类型是泛化的并且你不知道或是不关心泛型变量的值,你总是应该用 ? 通配符替换一个原始类型。原始类型仅应用在向后兼容和遗留的代码中。注意,但是你不可以在构造方法中使用。下面的代码是非法的:

List<?> l = new ArrayList<?>();


There is no sense in creating a List of unknown type. If you are creating it, you should know what kind of elements it will hold. You may later want to pass such a list to a method that does not care about its element type, but you need to specify an element type when you create it. If what you really want is a List that can hold any type of object, do this:

不可以用未知的类型创建一个 List 实例,如果你创建一个实例,你应该知道可以容纳的元素类型。接下来,你可能想传一个 list 到方法中,并不关心 list 中元素的类型,可惜你必需在创建它的时候指定一个元素的类型。如果你真得想创建一个可容纳任何类型对象的 List ,你可这么做:

List<Object> l = new ArrayList<Object>();


It should be clear from the printList( ) variants above that a List<?> is not the same thing as a List<Object> and that neither is the same thing as a raw List. A List<?> has two important properties that result from the use of a wildcard. First, consider methods like get() that are declared to return a value of the same type as the type parameter. In this case, that type is unknown, so these methods return an Object. Since all we need to do with the object is invoke its toString() method, this is fine for our needs.

这是非常清楚的,变种方法printList() 中的 List<?> 和 List<object> 是不同的。而且它们和原始类型的 List 一点也不一样。List<?>有两个重要的属性。首先,是用了通配符的结果。思考 get() 这样的方法,它被声明成返回值的类型和泛型参数指定的类型相同。在这个例子中,类型未知因此方法返回 Object 。既然我们所有要做的是调用 object 的 toString() 方法,那它一定会如我们所愿。

Second, consider List methods such as add() that are declared to accept an argument whose type is specified by the type parameter. This is the more surprising case: when the type parameter is unknown, the compiler does not let you invoke any methods that have a parameter of the unknown type because it cannot check that you are passing an appropriate value. A List<?> is effectively read-only since the compiler does not allow us to invoke methods like add( ), set(), and addAll( ).

第二,思考 List 的 add() 方法,它接受一个由泛型参数指定类型的参数。这是个很让人吃惊的情况:当泛型参数不未知时,编译器不会让我们调用任何方法,因为参数的类型未知,它不能检测你传递参数值的类型。List<?> 对仅仅是读是有效的,因为编译器不允许我们调用象 add(),set() 和 addAll() 此类的方法。

4.1.3.1 Bounded wildcards
4.1.3.1 边界通配符

Let's continue now with a slightly more complex variant of our original example. Suppose that we want to write a sumList() method to compute the sum of a list of Number objects. As before, we could use a raw List, but we would give up type safety and have to deal with unchecked warnings from the compiler. Or we could use a List<Number>, but then we wouldn't be able to call the method for a List<Integer> or List<Double>, types we are more likely to use in practice. But if we use a wildcard, we don't actually get the type safety that we want because we have to trust that our method will be called with a List whose type parameter is actually Number or a subclass and not, say, a String. Here's what such a method might look like:

接着让我们对最初的例子做个稍微复杂的变化。假设我们需要写一个 sumList() 方法,来计算一个 Number 类型 list 中元素的的和。在往常,我们用原始类型的 List ,但那会放弃类型安全检测,必须处理编译器的 unchecked 警告信息。或者我们可以用一个 List<Number>类型,但是那样做我们不能让方法用 List<Integer> 类型或是 List<Double> 类型,或是我们实际喜欢的类型的参数值。但是你如果用了通配符,却不能得到你想要的类型安全。因为我们必须委托调用参数为 List 类型的方法,它的实际的泛型参数值可能是 Number 或是它的子类,或者和它根本就不相关,比如说是一个 String 。这有一个这样的例子,我们可以看一下:

public static double sumList(List<?> list) {
double total = 0.0;
for(Object o : list) {
Number n = (Number) o; // A cast is required and may fail //需要造型,但可能会失败
total += n.doubleValue();
}
return total;
}


To fix this method and make it truly typesafe, we need to use a bounded wildcard that states that the type parameter of the List is an unknown type that is either Number or a subclass of Number. The following code does just what we want:

为了修订这个问题,真正做到类型安全,人们需要边界通配符。情况是这样的, List 的泛型参数是 Number 或是它的子类。下面的代码才是我们想要的:

public static double sumList(List<? extends Number> list) {
double total = 0.0;
for(Number n : list) total += n.doubleValue();
return total;
}


The type List<? extends Number> could be read as "List of unknown descendant of Number." It is important to understand that, in this context, Number is considered a descendant of itself.

类型 List<? extends Number> 可以读作"List of unknown descendant of Number.( Number 未知后裔的 List 类型)"。理解这一点是非常重要的。在这个语境中,Number 是自己的后裔。

Note that the cast is no longer required. We don't know the type of the elements of the list, but we know that they have an "upper bound" of Number so we can extract them from the list as Number objects. The use of a for/in loop obscures the process of extracting elements from a list somewhat. The general rule is that when you use a bounded wildcard with an upper bound, methods (like the get() method of List) that return a value of the type parameter use the upper bound. So if we called list.get( ) instead of using a for/in loop, we'd also get a Number. The prohibition on calling methods like list.add( ) that have arguments of the type parameter type still stands: if the compiler allowed us to call those methods we could add an Integer to a list that was declared to hold only Short values, for example.

注意,这里不再需要造型。我们不知道 list 元素的类型,但是我们知道他们都有一个"上界"类型是 Number ,因此我们可以把它们当作 Number 的对象拿出来。这里我们用隐式的 for/in 结构处理从 list 中拿到的每个元素。通常的规则是我们用上界通配符,让方法(象 list当中的 get() 方法)返回一个是上界泛型参数指定类型的值。例如,如果我们调用了 list.get() 方法,而不是用 for/in 结构,我会得到一个 Number 对象;但是仍然禁止调用有泛型参数的 list.add() 方法,如果编译器允许我们这样调用方法,我们就可以增加一个 Integer 的对象到容纳 Short 类型的 list 当中。

It is also possible to specify a lower-bounded wildcard using the keyword super instead of extends. This technique has a different impact on what methods can be called. Lower-bounded wildcards are much less commonly used than upper-bounded wildcards, and we discuss them later in the chapter.

当然你也可以有关键字 super 替换 extends 指定一个下界通配符。调用这样的方法的技术会带给我们不同的影响。下界通配符不如上界通配符用的多,我们会在章节的后面讨论。

4.1.4. Writing Generic Types and Methods
4.1.4.写泛类型和泛型方法

Creating a simple generic type is straightforward. First, declare your type variables by enclosing a comma-separated list of their names within angle brackets after the name of the class or interface. You can use those type variables anywhere a type is required in any instance fields or methods of the class. Remember, though, that type variables exist only at compile time, so you can't use a type variable with the runtime operators instanceof and new.

创建一个简单的泛类型是容易的。首先,在类名或是接口名后,用封闭的尖括号用逗号隔开声明一系列泛型变量。你可以在这个类的任何需要类型的地方,把泛型变量作为类型,应用到实例变量或是方法中。但是记住,泛型变量仅存在于编译期,因此你不能让泛型变量和运行的 instanceof 操作或是 new 操作连用

We begin this section with a simple generic type, which we will subsequently refine. This code defines a tree data structure that uses the type variable V to represent the type of the value held in each node of the tree:

这段开始我们定义了一个简单的泛类型,随后我们将重新定义它。代码定义了一个 tree 结构,用泛型变量 V 表示存储在 tree 中每个节点值的类型:

import java.util.*;

/**
* A tree is a data structure that holds values of type V.
* Each tree has a single value of type V and can have any number of
* branches, each of which is itself a Tree.
*/
/**
* 一个tree的数据结构,容纳类型为 V 的值。
* 每一个树有一个独立的 V 类型的值,可以有一定数据的分枝,它们又可以有自己的树
*/
public class Tree<V> {
// The value of the tree is of type V.
// 这个树的 vaule 类型是V
V value;

// A Tree<V> can have branches, each of which is also a Tree<V>
// 一个 Tree<V> 可以其他分枝,每个分析又都是一个 Tree<V>
List<Tree<V>> branches = new ArrayList<Tree<V>>();

// Here's the constructor. Note the use of the type variable V.
// 这是构造方法。注意这里用的是泛型变量 V
public Tree(V value) { this.value = value; }

// These are instance methods for manipulating the node value and branches.
// Note the use of the type variable V in the arguments or return types.
// 这些实例方法操作节点值和分枝。
// 注意这里在参数类型和返回值类型中用了泛型变量 V
V getValue() { return value; }
void setValue(V value) { this.value = value; }
int getNumBranches() { return branches.size(); }
Tree<V> getBranch(int n) { return branches.get(n); }
void addBranch(Tree<V> branch) { branches.add(branch); }
}


As you've probably noticed, the naming convention for type variables is to use a single capital letter. The use of a single letter distinguishes these variables from the names of actual types since real-world types always have longer, more descriptive names. The use of a capital letter is consistent with type naming conventions and distinguishes type variables from local variables, method parameters, and fields, which are sometimes written with a single lowercase letter. Collection classes like those in java.util often use the type variable E for "Element type." When a type variable can represent absolutely anything, T (for Type) and S are used as the most generic type variable names possible (like using i and j as loop variables).

可能你已经注意到了,泛型变量的命名约定是用一个大写字母表示。就是用一个单独的字母表示的变量去区别实际类型的变量名,因为真实的类型名总是很长的。一个大写字母的用法是和类型命名的约定相一致的,并这些泛型变量区别于局部判变量,方法参数和实例变量(这些变量有时用一个字母,不过是小写的)。象在包 java.util 中的Collection类用泛型变量 E 表示" Element type 元素类型"。在所有的泛型变量里,T(表示类型)和 S 可能是用得最多的变量名(就象循环变量 i 和 j 一样)。

Notice that the type variables declared by a generic type can be used only by the instance fields and methods (and nested types) of the type and not by static fields and methods. The reason, of course, is that it is instances of generic types that are parameterized. Static members are shared by all instances and parameterizations of the class, so static members do not have type parameters associated with them. Methods, including static methods, can declare and use their own type parameters, however, and each invocation of such a method can be parameterized differently. We'll cover this later in the chapter.

注意用泛类型声明的泛型变量仅被用作实例变量和方法类型(嵌套类型),不可用于静态域和静态方法。当然,理由是泛类型是实例初始化的。静态方法被类的所有实例共享,是类初始化的。因此静态方法不能有泛型参数相关联。方法(包括静态方法)的声明和使用可以是自己的泛型参数,不过每次调用的是一个不同参数化的方法。本章稍后我们会涉及到这个问题。

4.1.4.1 Type variable bounds
4.1.4.1 泛型变量边界

The type variable V in the declaration above of the TRee<V> class is unconstrained: TRee can be parameterized with absolutely any type. Often we want to place some constraints on the type that can be used: we might want to enforce that a type parameter implements one or more interfaces, or that it is a subclass of a specified class. This can be done by specifying a bound for the type variable. We've already seen upper bounds for wildcards, and upper bounds can also be specified for type variables using a similar syntax. The following code is the tree example rewritten to make tree objects Serializable and Comparable. In order to do this, the example uses a type variable bound to ensure that its value type is also Serializable and Comparable. Note how the addition of the
Comparable bound on V enables us to write the compareTo() method tree by guaranteeing the existence of a compareTo() method on V.[4]

在上面 TRee<V> 类中声明的泛型变量 V 是没有被约束的: Tree 可以被参数化成任何其他类型。我们经常会给要用的类型一定的约束:我们可能想这么做,让一个类型实现一个或多个接口,或者是一个指定类的。这个任务可以通过指定泛型变量的边界完成。我们已经见过上界通配符了,它可以用相似的语法被应用到泛型变量上。下面的 tree 代码应用了 Serializable 和 Comparable 进行了重写。为了完成任务,这个例子用了一个泛型变量通配符,以保证它的值类型也是 Serializable 和 Comparable的。注意,为了(通过)保证 V 中存在 compareTo() 方法,这种为 Comparable 增加 V 边界的变化是怎样让我们写一个有 compareTo()方法的 tree 的。

[4] The bound shown here requires that the value type V is comparable to itself, in other words, that it implements the Comparable interface directly. This rules out the use of types that inherit the Comparable interface from a superclass. We'll consider the Comparable interface in much more detail at the end of this section and present an alternative there.

这儿的边界要求值的类型 V 可以和自己比较,换句话说,它直接实现了 Comparable 接口。另外的一个用法是,让它从超类那里继承这个 Comparable 接口。我们将在这章结束时更详细的思考 Comparable 接口,现在这里两个中选择哪一种都行。

import java.io.Serializable;
import java.util.*;

public class Tree<V extends Serializable & Comparable<V>>
implements Serializable, Comparable<Tree<V>>
{
V value;
List<Tree<V>> branches = new ArrayList<Tree<V>>();

public Tree(V value) { this.value = value; }

// Instance methods
// 实例方法
V getValue() { return value; }
void setValue(V value) { this.value = value; }
int getNumBranches() { return branches.size(); }
Tree<V> getBranch(int n) { return branches.get(n); }
void addBranch(Tree<V> branch) { branches.add(branch); }

// This method is a nonrecursive implementation of Comparable<Tree<V>>
// It only compares the value of this node and ignores branches.
// 这里没有循环实现 Comparable<Tree<V>> 接口,只是比较这个节点的值,而忽略了分枝
public int compareTo(Tree<V> that) {
if (this.value == null && that.value == null) return 0;
if (this.value == null) return -1;
if (that.value == null) return 1;
return this.value.compareTo(that.value);
}

// javac -Xlint warns us if we omit this field in a Serializable class
// 如果在一个 Serializable 的类中省略这个域,如此编译 :javac -Xlint ,会得到警告
private static final long serialVersionUID = 833546143621133467L;
}


The bounds of a type variable are expressed by following the name of the variable with the word extends and a list of types (which may themselves be parameterized, as Comparable is). Note that with more than one bound, as in this case, the bound types are separated with an ampersand rather than a comma. Commas are used to separate type variables and would be ambiguous if used to separate type variable bounds as well. A type variable can have any number of bounds, including any number of interfaces and at most one class.

泛型变量边界通过其后的变量名、 extends 和类型列表(它们自己是可参数化的,比如 Comparable 就是)表达。注意,这个例子里是多个边界而不是一个,边界类型用之间用"&"分隔而不是","。","被用作泛型变量的分隔,再用作泛型变量边界分隔会产生歧义。一个泛型变量可以有相当数量的边界,包括一定量的接口和至多一个类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值