fundamentals\java\java5新特性-泛型(译)

Generics-泛型

目录

Generics-泛型

概述

Lesson: Generics泛型-课程

Introduction-引言

Defining Simple Generics-简单的泛型

Generics and Subtyping-泛型与子类型

Wildcards-通配符

Bounded Wildcards-有界通配符

Generic Methods-泛型方法

Interoperating with Legacy Code-与非泛型交互

Using Legacy Code in Generic Code-在泛型代码中使用非泛型

Erasure and Translation-消除和转换

Using Generic Code in Legacy Code-在泛型代码中使用非泛型

The Fine Print-附加条款

A Generic Class is Shared by All Its Invocations-泛型类由它所有的调用者共享

Casts and InstanceOf-类型强转和对象类型判断

Arrays-数组

Class Literals as Runtime-Type Tokens-类的字面类型做为运行时类型

More Fun with Wildcards-通配符的更多用法

Wildcard Capture-通配符匹配

Converting Legacy Code to Use Generics

Acknowledgements-致谢


概述

When you take an element out of a Collection, you must cast it to the type of element that is stored in the collection. Besides being inconvenient, this is unsafe. The compiler does not check that your cast is the same as the collection's type, so the cast can fail at run time.

当你从一个集合中取出一个元素时,你必须把它Cast做类型转换。除了不方便之外,这是不安全的。编译器Cast时不会检查集合中元素的类型,因此Cast在运行时可能会报错。

Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.

泛型提供了一种方法,可以让您与编译器交互集合的类型,这样集合的类型可以被检查。一旦编译器知道集合的元素类型,编译器就可以自始自终对集合进行检查,并且可以检查insert类型,并从集合中取值时自动Cast类型转换。

// Here is a simple example taken from the existing Collections tutorial:

// 下面是一个从现有的集合教程tutorial中选取的简单示例:

// Removes 4-letter words from c. Elements must be strings

// 从集合c中移除4个字母的的元素,元素类型必须为String

static void expurgate(Collection c) {

    for (Iterator i = c.iterator(); i.hasNext(); )

      if (((String) i.next()).length() == 4) // cast

        i.remove();

}

// Here is the same example modified to use generics:

// 下面是对同一个示例使用泛型的栗子:

// Removes the 4-letter words from c 从集合c中移除4个字母的的元素

static void expurgate(Collection<String> c) {

    for (Iterator<String> i = c.iterator(); i.hasNext(); )

      if (i.next().length() == 4)

        i.remove();

}

 

When you see the code <Type>, read it as "of Type"; the declaration above reads as "Collection of String c." The code using generics is clearer and safer. We have eliminated an unsafe cast and a number of extra parentheses. More importantly, we have moved part of the specification of the method from a comment to its signature, so the compiler can verify at compile time that the type constraints are not violated at run time. Because the program compiles without warnings, we can state with certainty that it will not throw a ClassCastException at run time. The net effect of using generics, especially in large programs, is improved readability and robustness.

当你看到代码<Type>的时候,把它读成类型”;上面的声明读为字符串c的集合。使用泛型的代码更加清晰和安全。我们已经排除了不安全的一些额外的类型转换操作。更重要的是,我们已经将该方法的部分约束从注释转移到它的签名,因此编译器可以在编译时实时验证元素类型是否合法。因为程序在没有警告的情况下编译,所以我们可以肯定地说,它不会在运行时抛出ClassCastException。使用泛型的最终效果,特别是在大型程序中,将会是提高了可读性和健壮性。

To paraphrase Generics Specification Lead Gilad Bracha, when we declare c to be of type Collection<String>, this tells us something about the variable c that holds true wherever and whenever it is used, and the compiler guarantees it (assuming the program compiles without warnings). A cast, on the other hand, tells us something the programmer thinks is true at a single point in the code, and the VM checks whether the programmer is right only at run time.

用泛型规范来说明Gilad Bracha,当我们声明cCollection<String>的时候,这告诉我们变量c中的元素无论任何何地都是String,编译器确保(假设程序编译没有警告)集合c中的元素类型。另一方面,类型转换cast告诉我们程序员在开发代码时,认为在类型上程序是正确的,而VM只在运行时才会做检查。

 

While the primary use of generics is collections, there are many other uses. "Holder classes," such as WeakReference and ThreadLocal, have all been generified, that is, they have been retrofitted to make use of generics. More surprisingly, class Class has been generified. Class literals now function as type tokens, providing both run-time and compile-time type information. This enables a style of static factories exemplified by the getAnnotation method in the new AnnotatedElement interface:

虽然泛型的主要用途是集合,但还有许多其他用途。“Holder classes”,如WeakReference ThreadLocal,都已被泛型化,也就是说,它们已经被重新设计以使用泛型。更令人惊讶的是,Class类也已经被泛化了。Class关键字现在可以作为类型标识,在运行时或者编译时提供类型信息。这导致了,使用AnnotatedElement.getAnnotation() 方法的静态工厂的一种实现:

// a style of static factories exemplified by the getAnnotation method in the new  interface:

// 使用AnnotatedElementgetAnnotation方法的静态工厂的一种实现:

<T extends Annotation> T getAnnotation(Class<T> annotationType);

 

// This is a generic method.

// It infers the value of its type parameter T from its argument,

// and returns an appropriate instance of T, as illustrated by the following snippet:

// 这是一个通用的方法。它从它的传入参数中推断出它的参数T(类型参数)的值,并返回一个适当的T实例,如下面的片段所示:

Author a = Othello.class.getAnnotation(Author.class);

 

Prior to generics, you would have had to cast the result to Author. Also you would have had no way to make the compiler check that the actual parameter represented a subclass of Annotation.

在泛型之前,您必须将结果转换为Author。你也没有办法让编译器检查实际参数是否是Annotation的子类。

 

Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler. The main advantage of this approach is that it provides total interoperability between generic code and legacy code that uses non-parameterized types (which are technically known as raw types). The main disadvantages are that parameter type information is not available at run time, and that automatically generated casts may fail when interoperating with ill-behaved legacy code. There is, however, a way to achieve guaranteed run-time type safety for generic collections even when interoperating with ill-behaved legacy code.

泛型是通过类型擦除实现的:泛型的类型信息只在编译时确定,之后由编译器擦除。这种方法的主要优点是它提供了泛型代码和(非参数化类型,技术上称为原始类型的)未使用泛型代码之间的完全的协作性。主要的缺点是参数的类型信息在运行时不可用,并且当与行为不佳的遗留代码交互时,自动生成的类型转换可能会失败。然而,即使在与行为不规范的遗留代码进行交互时,也有一种方法可以实现泛型集合运行时的类型安全性保证。

The java.util.Collections class has been outfitted with wrapper classes that provide guaranteed run-time type safety. They are similar in structure to the synchronized and unmodifiable wrappers. These "checked collection wrappers" are very useful for debugging. Suppose you have a set of strings, s, into which some legacy code is mysteriously inserting an integer. Without the wrapper, you will not find out about the problem until you read the problem element from the set, and an automatically generated cast to String fails. At this point, it is too late to determine the source of the problem. If, however, you replace the declaration:

java.util.Collections 类已经配备了包装类,它们提供运行时类型安全性保证。它们在结构上与synchronizedunmodifiable包装类相似。这些集合类型检查包装类对于调试非常有用。假设你有一个字符串集合,s,一些旧代码正神秘地insert一个整数。如果没有包装类,您将不会发现问题,直到您从集合中读取问题元素,并且自动生成的类型转换器转换 整数 String才会失败。在这一点上,现在确定问题的根源已经太迟了。然而,如果你替换了声明:

// java.util.Collections 类已经配备了包装类,它们提供运行时类型安全性保证。

// 假设你有一个字符串集合,s,一些旧代码正神秘地insert一个整数。如果没有包装类,您将不会发现问题

Set<String> s = new HashSet<String>();

// with this declaration:

// 如果这样定义:

// the collection will throw a ClassCastException at the point where the legacy code attempts to insert the integer.

// The resulting stack trace will allow you to diagnose and repair the problem.

// 集合将抛出ClassCastException,在旧代码试图insert整数时。产生的堆栈错误信息跟踪将允许您诊断和修复问题。

Set<String> s = Collections.checkedSet(new HashSet<String>(), String.class);

 

You should use generics everywhere you can. The extra effort in generifying code is well worth the gains in clarity and type safety. It is straightforward to use a generic library, but it requires some expertise to write a generic library, or to generify an existing library. There is one caveat: You may not use generics (or any other Tiger features) if you intend to deploy the compiled code on a pre-5.0 virtual machine.

您可以在任何地方使用泛型。在清晰和类型安全方在泛化代码中所做的额外工作是非常值的。使用泛型库是很简单的,但是它需要一些专业经验来编写一个泛型库,或者让一个现有的库泛化。有一个警告: 如果您打算将编译后的代码部署在一个5.0以前的虚拟机上,您可能不能使用泛型(或5.0 Tiger)的任何其他特性)。

If you are familiar with C++'s template mechanism, you might think that generics are similar, but the similarity is superficial. Generics do not generate a new class for each specialization, nor do they permit "template metaprogramming.

如果您熟悉C++的模板机制,您可能会认为泛型是相似的,但是相似是肤浅的。泛型不会为每个专门化生成一个新类,也不允许模板元编程

There is much more to learn about generics. See the Generics lesson in the Java Tutorials.

关于泛型还有很多东西要学。参见Java教程中的泛型课程。

Lesson: Generics泛型-课程

Introduced in J2SE 5.0, this long-awaited enhancement to the type system allows a type or method to operate on objects of various types while providing compile-time type safety. It adds compile-time type safety to the Collections Framework and eliminates the drudgery of casting.

J2SE 5.0中引入了这种期待已久的对类型系统的增强,它允许类型或方法对各种类型的对象进行操作,同时提供编译时类型安全。它为集合框架添加了编译时类型安全,并消除了类型转换的苦差事。

Introduction-引言

JDK 5.0 introduces several new extensions to the Java programming language. One of these is the introduction of generics.

JDK 5.0引入了Java编程语言的几个新扩展。其中之一就是泛型的引入。

This trail is an introduction to generics. You may be familiar with similar constructs from other languages, most notably C++ templates. If so, you'll see that there are both similarities and important differences. If you are unfamiliar with look-a-alike constructs from elsewhere, all the better; you can start fresh, without having to unlearn any misconceptions.

这是对泛型的介绍。您可能熟悉其他语言的类似结构,最著名的是C++模板。如果是这样,你会发现两者都有相似之处和重要的区别。如果你不熟悉其他地方的类似的结构,那就更好了;你可以重新开始,而不需要消除任何误解。

Generics allow you to abstract over types. The most common examples are container types, such as those in the Collections hierarchy.

泛型允许您对类型进行抽象。最常见的例子是容器类型,比如这些集合层次结构中的容器类型。

// Here is a typical usage of that sort:

// 这是一种典型的用法:

List myIntList = new LinkedList(); // 1

myIntList.add(new Integer(0)); // 2

Integer x = (Integer) myIntList.iterator().next(); // 3

// The cast on line 3 is slightly annoying.

// Typically, the programmer knows what kind of data has been placed into a particular list.

// However, the cast is essential.

// The compiler can only guarantee that an Object will be returned by the iterator.

// To ensure the assignment to a variable of type Integer is type safe, the cast is required.

// 3行的类型强转有点烦人。通常,程序员知道在特定列表中放置了什么样的数据。

// 然而,强转是必不可少的。编译器只能保证一个对象会被迭代器返回。

// 为了确保对Integer类型的变量的赋值是类型安全的,所以必须使用类型转换。

 

// Of course, the cast not only introduces clutter.

// It also introduces the possibility of a run time error, since the programmer may be mistaken.

// 当然,强转不仅会带来混乱。它还引入了运行时错误的可能性,因为程序员可能是错误的。

 

// What if programmers could actually express their intent, and mark a list as being restricted to contain a particular data type?

// This is the core idea behind generics. Here is a version of the program fragment given above using generics:

// 如果程序员能够明确他们的意图,并将一个列表标记为包含特定的数据类型,那该怎么办呢?

// 这是泛型背后的核心思想。下面是使用泛型给出的程序片段的一个版本:

List<Integer> myIntList = new LinkedList<Integer>(); // 1'

myIntList.add(new Integer(0)); // 2'

Integer x = myIntList.iterator().next(); // 3'

// Notice the type declaration for the variable myIntList.

// It specifies that this is not just an arbitrary List, but a List of Integer, written List<Integer>.

// We say that List is a generic interface that takes a type parameter--in this case, Integer.

// We also specify a type parameter when creating the list object.

// 请注意变量myIntList的类型声明。它指定这不仅仅是一个任意的列表,而是一个整型的列表,定义为List<Integer>

// 我们说List是一个泛型的接口,它接受一个Type参数——在本例中是Integer。我们在创建List实例时也指定了Type参数。

// Note, too, that the cast on line 3' is gone.

// 还要注意,第3行的强转已经不需要了

 

Now, you might think that all we've accomplished is to move the clutter around. Instead of a cast to Integer on line 3, we have Integer as a type parameter on line 1'. However, there is a very big difference here. The compiler can now check the type correctness of the program at compile-time. When we say that myIntList is declared with type List<Integer>, this tells us something about the variable myIntList, which holds true wherever and whenever it is used, and the compiler will guarantee it. In contrast, the cast tells us something the programmer thinks is true at a single point in the code.

现在,你可能会认为我们所做的一切都是把讨厌的去掉类型强转。相比于在第3行中做类型转换,我们在第1行中定义整数作为类型参数。编译器现在可以在编译时检查程序的类型正确性。当我们说myIntList是用List<Integer>来声明的时候,这就告诉了我们一些关于变量myIntList的一些其他东西,在任何时候,编译器会保证其中元素类型Integer相反,cast类型强转告诉我们程序员认为在代码的类型是Integer

The net effect, especially in large programs, is improved readability and robustness.

最终效果,特别是在大型程序中,提高了可读性和健壮性

Defining Simple Generics-简单的泛型

// Here is a small excerpt from the definitions of the interfaces List and Iterator in package java.util:

// 这里是摘录自java.util包中的列表和迭代器接口的定义:

public interface List <E> {

    void add(E x);

    Iterator<E> iterator();

}

 

public interface Iterator<E> {

    E next();

    boolean hasNext();

}

 

 

This code should all be familiar, except for the stuff in angle brackets. Those are the declarations of the formal type parameters of the interfaces List and Iterator.

这段代码应该都很熟悉,除了尖括号里的东西。这些是接口列表和迭代器的正式类型参数的声明。

Type parameters can be used throughout the generic declaration, pretty much where you would use ordinary types (though there are some important restrictions; see the section The Fine Print.

类型参数可以在整个泛型声明中比较常见,比使用无参类型多得多(尽管有一些重要的限制;参考:细则)

In the introduction, we saw invocations of the generic type declaration List, such as List<Integer>. In the invocation (usually called a parameterized type), all occurrences of the formal type parameter (E in this case) are replaced by the actual type argument (in this case, Integer).

在引言中,我们见识了泛型列表的调用示例,例如List<integer>。在调用(通常称为参数化类型)时,所有正式类型参数(在本例中为E)都将被实际的类型参数替换(在本例中为整数)

// You might imagine that List<Integer> stands for a version of List where E has been uniformly replaced by Integer:

// 你可以想象一下,这个列表代表了这样一个列表,其中EInteger替换了:

public interface IntegerList {

    void add(Integer x);

    Iterator<Integer> iterator();

}

 

This intuition can be helpful, but it's also misleading.

这种直觉是有帮助的,但它也具有误导性

It is helpful, because the parameterized type List<Integer> does indeed have methods that look just like this expansion.

它是有帮助的,因为参数化类型List<Integer>确实有一些方法看起来就像这个扩展一样。

It is misleading, because the declaration of a generic is never actually expanded in this way. There aren't multiple copies of the code--not in source, not in binary, not on disk and not in memory. If you are a C++ programmer, you'll understand that this is very different than a C++ template.

这是一种误导,因为泛型从来没有以这种方式扩展。代码没有多个副本——在源代码中没有,在编译后的二进制字节码中没有,在磁盘上没有,在内存中也没有。如果您是一个C++程序员,您就会明白这与C++模板非常不同。

A generic type declaration is compiled once and for all, and turned into a single class file, just like an ordinary class or interface declaration.

泛型类型声明只进行一次性编译,并将其转换为单个类文件,就像普通类或接口声明一样。

Type parameters are analogous to the ordinary parameters used in methods or constructors. Much like a method has formal value parameters that describe the kinds of values it operates on, a generic declaration has formal type parameters. When a method is invoked, actual arguments are substituted for the formal parameters, and the method body is evaluated. When a generic declaration is invoked, the actual type arguments are substituted for the formal type parameters.

类型参数和方法或构造函数中使用的普通参数类似。就像一个方法有正式的参数来描述它所操作的值的类型一样,泛型声明有正式的类型参数。当一个方法被调用时,实际的参数被替换为正式的参数,并且方法主体被赋值。当调用泛型声明时,实际的类型参数将被替换为实际类型参数。

A note on naming conventions. We recommend that you use pithy (single character if possible) yet evocative names for formal type parameters. It's best to avoid lower case characters in those names, making it easy to distinguish formal type parameters from ordinary classes and interfaces. Many container types use E, for element, as in the examples above. We'll see some additional conventions in later examples.

关于命名约定的备注。我们建议您使用简练的(单个字母如果可能的话),但是对于正式的类型参数,您可以使用有意义的名字。最好避免这些泛型名称中的小写字符,这样就可以很容易地将正规类型参数与普通类和接口区分开来。许多类型参数使用E,代表元素,如上面的例子。我们将在后面的示例中看到一些额外的约定。

Generics and Subtyping-泛型与子类型

// Let's test your understanding of generics. Is the following code snippet legal?

// 让我们测试一下您对泛型的理解。下面的代码片段是合法的吗?

List<String> ls = new ArrayList<String>(); // 1

List<Object> lo = ls; // 2

// Line 1 is certainly legal.

// The trickier part of the question is line 2.

// This boils down to the question: is a List of String a List of Object.

// Most people instinctively answer, "Sure!"

// 第一行当然是合法的。问题的棘手部分是第2行。

// 这可以归结为一个问题:字符串列表转对象列表。大多数人本能地回答:当然!

 

// Well, take a look at the next few lines:

// 好吧,看下几行:

lo.add(new Object()); // 3

String s = ls.get(0); // 4: Attempts to assign an Object to a String!

 

Here we've aliased ls and lo. Accessing ls, a list of String, through the alias lo, we can insert arbitrary objects into it. As a result ls does not hold just Strings anymore, and when we try and get something out of it, we get a rude surprise.

在这里,我们给ls取别名lo。通过别名lo,访问ls,一个字符串List,我们可以将任意对象插入其中。因此,ls不再只持有字符串,当我们试图从它中得到一些东西时,我们会得到一个未知的惊喜。

The Java compiler will prevent this from happening of course. Line 2 will cause a compile time error.

当然,Java编译器会阻止这种情况的发生。第2行将导致编译时错误

In general, if Foo is a subtype (subclass or subinterface) of Bar, and G is some generic type declaration, it is not the case that G<Foo> is a subtype of G<Bar>. This is probably the hardest thing you need to learn about generics, because it goes against our deeply held intuitions.

总之,如果FooBar的子类型(子类或子接口),G是一些泛型类型的声明,那么,G<Foo>并不是G<Bar>的子类型。这可能是您需要了解泛型的最困难的事情,因为它违背了我们根深蒂固的直觉。

We should not assume that collections don't change. Our instinct may lead us to think of these things as immutable.

我们不应该假设集合不会改变。我们的直觉可能会让我们认为这些东西是不可变的。

For example, if the department of motor vehicles supplies a list of drivers to the census bureau, this seems reasonable. We think that a List<Driver> is a List<Person>, assuming that Driver is a subtype of Person. In fact, what is being passed is a copy of the registry of drivers. Otherwise, the census bureau could add new people who are not drivers into the list, corrupting the DMV's records.

栗如,如果机动车部门向人口普查局提供一份司机名单,这似乎是合理的。我们认为 List<Driver>是一个 List<Person>,假设DriverPerson的子类型,事实上,传递给普查局的是在机动车部门注册的司机的另一份副本(另一个List)。否则,人口普查局可能会把那些不是司机的新员工加入到名单中,从而破坏了DMV(机动车部门)的记录。

To cope with this sort of situation, it's useful to consider more flexible generic types. The rules we've seen so far are quite restrictive.

为了应对这种情况,考虑更灵活的泛型是很有用的。到目前为止,我们所看到的规则是相当严格的。

Wildcards-通配符

// Consider the problem of writing a routine that prints out all the elements in a collection.

// Here's how you might write it in an older version of the language (i.e., a pre-5.0 release):

// 考虑编写一个程序,打印出集合中的所有元素。下面是较老版本的语言中(例如,pre - 5.0版本):

void printCollection(Collection c) {

    Iterator i = c.iterator();

    for (k = 0; k < c.size(); k++) {

        System.out.println(i.next());

    }

}

// And here is a naive attempt at writing it using generics (and the new for loop syntax):

// 这是一个使用泛型(和新的for循环语法)编写它的简单尝试:

void printCollection(Collection<Object> c) {

    for (Object e : c) {

        System.out.println(e);

    }

}

 

The problem is that this new version is much less useful than the old one. Whereas the old code could be called with any kind of collection as a parameter, the new code only takes Collection<Object>, which, as we've just demonstrated, is not a supertype of all kinds of collections!

问题是,这个新版本不比旧版本有用。旧的代码可以用任何类型的集合作为参数来调用,但是新的代码只能使用Collection<Object>正如我们刚才所展示的,它并不是所有类型的集合的超类型!

So what is the supertype of all kinds of collections? It's written Collection<?> (pronounced "collection of unknown"), that is, a collection whose element type matches anything. It's called a wildcard type for obvious reasons. We can write:

那么,所有类型的集合的超类型是什么呢?它写做 Collection<?> (读作未知的集合),也就是说,一个元素类型匹配任何东西的集合。显而易见的,它被称为通配符类型。我们可以这样写

// Collection<?> 一个元素类型匹配任何东西的集合

void printCollection(Collection<?> c) {

    for (Object e : c) {

        System.out.println(e);

    }

}

// and now, we can call it with any type of collection.

// Notice that inside printCollection(),

// we can still read elements from c and give them type Object.

// This is always safe, since whatever the actual type of the collection,

// it does contain objects. It isn't safe to add arbitrary objects to it however:

// 现在,我们可以用任何类型的集合来调用它。

// 请注意,在printCollection()中,我们仍然可以从c读取元素并给它们类型对象。

// 这总是安全的,因为无论集合的实际类型是什么,它都包含对象。然而,向它添加任意对象是不安全的:

Collection<?> c = new ArrayList<String>();

c.add(new Object()); // Compile time error

 

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

因为我们不知道c的元素类型是什么,所以我们不能向它添加对象。add()方法接受E类型的参数,即收藏品的元素类型。当实际类型参数是什么时候?它代表着某种未知的类型。我们传递给add()方法的任何参数都必须是这种未知类型的子类型。既然我们不知道那是什么类型,我们就不能把任何东西传递进去。唯一的例外是null,它是每种类型的成员。

On the other hand, given a List<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.

另一方面,给定一个List<?>我们可以调用get()并得到结果。结果类型是一个未知类型,但我们总是知道它是一个对象。因此,可以安全地将get()的结果指派给类型Object的变量,或者将其作为Object类型的参数进行传递。

Bounded Wildcards-有界通配符

// Consider a simple drawing application that can draw shapes such as rectangles and circles.

// To represent these shapes within the program, you could define a class hierarchy such as this:

// 考虑一个简单的绘图应用程序,它可以绘制矩形和圆形等形状。

// 为了在程序中表示这些形状,您可以定义一个类层次结构,例如:

public abstract class Shape {

    public abstract void draw(Canvas c);

}

 

public class Circle extends Shape {

    private int x, y, radius;

    public void draw(Canvas c) {

        ...

    }

}

 

public class Rectangle extends Shape {

    private int x, y, width, height;

    public void draw(Canvas c) {

        ...

    }

}

// These classes can be drawn on a canvas:

// 这些类可以绘制在画布上:

public class Canvas {

    public void draw(Shape s) {

        s.draw(this);

   }

}

 

// Any drawing will typically contain a number of shapes.

// Assuming that they are represented as a list, it would be convenient to have a method in Canvas that draws them all:

// 任何绘画通常都包含许多形状。假设它们被表示为一个列表,那么在画布上有一个方法可以很方便地将它们全部画出来:

public void drawAll(List<Shape> shapes) {

    for (Shape s: shapes) {

        s.draw(this);

   }

}

 

// Now, the type rules say that drawAll() can only be called on lists of exactly Shape:

// it cannot, for instance, be called on a List<Circle>.

// That is unfortunate, since all the method does is read shapes from the list,

// so it could just as well be called on a List<Circle>.

// What we really want is for the method to accept a list of any kind of shape:

// 现在,类型规则规定drawAll()方法,只能被List<Shape>调用:例如,它不被List<Circle>调用。

// 但是很不幸的,因为所有的方法都只是从列表中读取Shape,所以我们希望它也可以被List<Circle>调用。

// 我们真正想要的是让方法接受任何形式的列表:

public void drawAll(List<? extends Shape> shapes) {

    ...

}

 

// There is a small but very important difference here:

// we have replaced the type List<Shape> with List<? extends Shape>.

// Now drawAll() will accept lists of any subclass of Shape, so we can now call it on a List<Circle> if we want.

// 这里有一个很小但非常重要的区别:我们已经用List<Shape>代替了List<?,Shape>

// 现在drawAll()将接受任何Shape子类的列表,所以如果我们愿意,我们现在可以在List<Circle>中调用它。

 

// List<? extends Shape> is an example of a bounded wildcard.

// The ? stands for an unknown type, just like the wildcards we saw earlier.

// However, in this case, we know that this unknown type is in fact a subtype of Shape.

// (Note: It could be Shape itself, or some subclass; it need not literally extend Shape.)

// We say that Shape is the upper bound of the wildcard.

// List<? extends Shape>是一个有界通配符的例子。?代表未知类型,就像我们之前看到的通配符一样。

// 然而,在这种情况下,我们知道这种未知类型实际上是形状的子类型。(

// 注意:它可以是形状本身,也可以是某个子类;它不需要真正地扩展形状。)我们说这个形状是通配符的上界。

 

// There is, as usual, a price to be paid for the flexibility of using wildcards.

// That price is that it is now illegal to write into shapes in the body of the method.

// For instance, this is not allowed:

// 与往常一样,使用通配符的灵活性也需要付出代价。这个价格是,在这个方法的主体中添加元素Shape是违法的。

// 例如,这是不允许的:

public void addRectangle(List<? extends Shape> shapes) {

    // Compile-time error!

    shapes.add(0, new Rectangle());

}

 

You should be able to figure out why the code above is disallowed. The type of the second parameter to shapes.add() is extends Shape-- an unknown subtype of Shape. Since we don't know what type it is, we don't know if it is a supertype of Rectangle; it might or might not be such a supertype, so it isn't safe to pass a Rectangle there.

您应该能够弄清楚为什么上面的代码是不允许的。shap.add()的第二个参数的类型是 extends Shape—一个Shape类的未知子类。因为我们不知道它是什么类型,我们不知道它是否是矩形类或者它的超类型; 它可能也可能不是这样的超类型,所以在那里传递一个矩形是不安全的。

Bounded wildcards are just what one needs to handle the example of the DMV passing its data to the census bureau. Our example assumes that the data is represented by mapping from names (represented as strings) to people (represented by reference types such as Person or its subtypes, such as Driver). Map<K,V> is an example of a generic type that takes two type arguments, representing the keys and values of the map.

有界通配符是处理DMV(车管局)将其数据传递给人口普查局的例子的需要。我们的例子假设数据是通过,从names(字符串)映射到人(用引用类型来表示的,例如Person或其子类型,例如Driver)。Map<K,V>是使用两个参数的泛型的实例,代表了mapkeyvalues

// Again, note the naming convention for formal type parameters--K for keys and V for values.

// 同样,请注意参数的命名约定————K和值V

public class Census { // 人口普查

    public static void addRegistry(Map<String, ? extends Person> registry) {

}

...

 

Map<String, Driver> allDrivers = ... ;

Census.addRegistry(allDrivers);

 

Generic Methods-泛型方法

// Consider writing a method that takes an array of objects

// and a collection and puts all objects in the array into the collection.

// Here's a first attempt:

// 考虑编写一个方法,该方法接受物件数组和一个集合,并将数组中的所有对象放入集合中。

// 这里有一个第一次尝试:

static void fromArrayToCollection(Object[] a, Collection<?> c) {

    for (Object o : a) {

        c.add(o); // compile-time error

    }

}

// By now, you will have learned to avoid the beginner's mistake

// of trying to use Collection<Object> as the type of the collection parameter.

// You may or may not have recognized that using Collection<?> isn't going to work either.

// Recall that you cannot just shove objects into a collection of unknown type.

// 到目前为止,您已经学会了避免初学者的错误,即尝试使用托收对象作为集合参数的类型。

// 您可能或可能没有认识到使用Collection<?> 也不会起作用。

// 回想一下,您不能仅仅将对象推入未知类型的集合中。

 

// The way to do deal with these problems is to use generic methods.

// Just like type declarations, method declarations can be generic—that is,

// parameterized by one or more type parameters.

// 处理这些问题的方法是使用泛型方法。就像类型声明一样,

// 方法声明可以是通用的,由一个或多个类型参数参数化。

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {

    for (T o : a) {

        c.add(o); // Correct

    }

}

// We can call this method with any kind of collection

// whose element type is a supertype of the element type of the array.

// 我们可以用任何类型的集合来调用这个方法,它的元素类型是数组的元素类型的超类型。

Object[] oa = new Object[100];

Collection<Object> co = new ArrayList<Object>();

 

// T inferred to be Object

// T 引用Object

fromArrayToCollection(oa, co);

 

String[] sa = new String[100];

Collection<String> cs = new ArrayList<String>();

 

// T inferred to be String

fromArrayToCollection(sa, cs); // String

 

// T inferred to be Object

fromArrayToCollection(sa, co);  // Object

 

Integer[] ia = new Integer[100];

Float[] fa = new Float[100];

Number[] na = new Number[100];

Collection<Number> cn = new ArrayList<Number>();

 

// T inferred to be Number

fromArrayToCollection(ia, cn);

 

// T inferred to be Number

fromArrayToCollection(fa, cn);

 

// T inferred to be Number

fromArrayToCollection(na, cn);

 

// T inferred to be Object

fromArrayToCollection(na, co);

 

// compile-time error

fromArrayToCollection(na, cs);

 

// Notice that we don't have to pass an actual type argument to a generic method.

// The compiler infers the type argument for us, based on the types of the actual arguments.

// It will generally infer the most specific type argument that will make the call type-correct.

// 请注意,我们不需要将实际类型参数传递给泛型方法。编译器根据实际参数的类型推断出我们的类型参数。

// 它通常会推断出使调用类型正确的最具体的类型参数。

 

One question that arises is: when should I use generic methods, and when should I use wildcard types? To understand the answer, let's examine a few methods from the Collection libraries.

出现的一个问题是:什么时候应该使用泛型方法,什么时候应该使用通配符类型?为了理解这个答案,让我们查看下Collection库中一些方法。

// One question that arises is: when should I use generic methods, and when should I use wildcard types?

// To understand the answer, let's examine a few methods from the Collection libraries.

// 出现的一个问题是:什么时候应该使用泛型方法,什么时候应该使用通配符类型?

// 为了理解这个答案,让我们查看下Collection库中一些方法。

interface Collection<E> {

    public boolean containsAll(Collection<?> c);

    public boolean addAll(Collection<? extends E> c);

}

// We could have used generic methods here instead:

// 我们也可以使用泛型方法:

interface Collection<E> {

    public <T> boolean containsAll(Collection<T> c);

    public <T extends E> boolean addAll(Collection<T> c);

    // Hey, type variables can have bounds too!

    // 嘿,类型变量也可以有界限!

}

// However, in both containsAll and addAll, the type parameter T is used only once.

// The return type doesn't depend on the type parameter,

// nor does any other argument to the method (in this case, there simply is only one argument).

// This tells us that the type argument is being used for polymorphism;

// its only effect is to allow a variety of actual argument types to be used at different invocation sites.

// If that is the case, one should use wildcards.

// Wildcards are designed to support flexible subtyping, which is what we're trying to express here.

// 然而,在containsAlladdAll中,类型参数T只被使用一次。

// return类型不依赖于类型参数,也不依赖于方法其他参数(在本例中,只有一个参数)。

// 这告诉我们类型参数主要用于多态性;

// 它的唯一作用是允许在不同的地方(return ,parameters)使用各种实际的参数类型。

// 如果是上栗这种情况,应该使用通配符。通配符的设计目的是支持灵活的子类型,这正是我们在这里想要的。

 

// Generic methods allow type parameters to be used to express dependencies

// among the types of one or more arguments to a method and/or its return type.

// If there isn't such a dependency, a generic method should not be used.

// 泛型方法允许使用类型参数来表示方法的一个或多个参数类型的依赖关系或和它的返回类型依赖关系。

// 如果没有这种依赖关系,就不应该使用泛型方法。

 

It is possible to use both generic methods and wildcards in tandem.

可以同时使用通用方法和通配符么。

// It is possible to use both generic methods and wildcards in tandem.

// Here is the method Collections.copy():

// 可以同时使用通用方法和通配符。下面是方法Collections.copy()

class Collections {

    public static <T> void copy(List<T> dest, List<? extends T> src) {

    ...

}

// Note the dependency between the types of the two parameters.

// Any object copied from the source list, src,

// must be assignable to the element type T of the destination list, dst.

// So the element type of src can be any subtype of T—we don't care which.

// The signature of copy expresses the dependency using a type parameter,

// but uses a wildcard for the element type of the second parameter.

// 请注意这两个参数类型之间的依赖关系。任何从源列表中复制的对象,src

// 都必须可以分配到目标列表,匹配其元素类型Tdst

// 所以src的元素类型可以是T的任何子类型————我们不关心它。

// copy方法由于这种依赖关系而是用了类型参数T,但是我们的第二个参数使用了通配符。

 

// We could have written the signature for this method another way, without using wildcards at all:

// 我们本可以用另一种方法来写这个方法的签名,而不需要使用通配符:

class Collections {

    public static <T, S extends T> void copy(List<T> dest, List<S> src) {

    ...

}

 

This is fine, but while the first type parameter is used both in the type of dst and in the bound of the second type parameter, SS itself is only used once, in the type of src—nothing else depends on it. This is a sign that we can replace S with a wildcard. Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible.

这很好,但是当第一个类型参数Tdst的类型和第二个类型参数的绑定中同时使用时,S本身只使用一次,在src的类型中——没有其他的依赖于它。这是一个信号,我们可以用通配符替换S。使用通配符比声明显式类型参数更清楚、更简洁,因此在可能的情况下应该选择使用通配符。

Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays. Here is an example.

通配符还有一个优点,可以在方法签名之外使用,如字段类型、局部变量和数组。这是一个例子

// Returning to our shape drawing problem, suppose we want to keep a history of drawing requests.

// We can maintain the history in a static variable inside class Shape,

// and have drawAll() store its incoming argument into the history field.

// 回到我们的图形绘制问题,假设我们想要保留绘制请求的历史。我们可以在Shape类中通过静态变量维护绘制的历史,

// 并拥有drawAll() 将其传入参数存储到历史字段中。

static List<List<? extends Shape>>

    history = new ArrayList<List<? extends Shape>>();

 

public void drawAll(List<? extends Shape> shapes) {

    history.addLast(shapes);

    for (Shape s: shapes) {

        s.draw(this);

    }

}

 

Finally, again let's take note of the naming convention used for the type parameters. We use T for type, whenever there isn't anything more specific about the type to distinguish it. This is often the case in generic methods. If there are multiple type parameters, we might use letters that neighbor T in the alphabet, such as S. If a generic method appears inside a generic class, it's a good idea to avoid using the same names for the type parameters of the method and class, to avoid confusion. The same applies to nested generic classes.

最后,让我们再次注意类型参数所使用的命名约定。我们用T来表示类型,无论有没有更具体的类型特征以做区别。在泛型方法中经常是这种情况。如果有多个类型的参数,我们可能会使用字母中相邻的字母,比如s。如果一个泛型方法出现在泛型类中,那么最好避免为方法和类的类型参数使用相同的名称,以避免混淆。这同样适用于嵌套泛型类。

Interoperating with Legacy Code-与非泛型交互

Until now, all our examples have assumed an idealized world, where everyone is using the latest version of the Java programming language, which supports generics.

到目前为止,我们所有的例子都假设了一个理想化的世界,每个人都在使用最新版本的Java编程语言,它支持泛型。

Alas, in reality this isn't the case. Millions of lines of code have been written in earlier versions of the language, and they won't all be converted overnight.

唉,事实上,事实并非如此。在早期版本的语言中已经写了数百万行代码,它们不会在一夜之间转换。

Later, in the Converting Legacy Code to Use Generics section, we will tackle the problem of converting your old code to use generics. In this section, we'll focus on a simpler problem: how can legacy code and generic code interoperate? This question has two parts: using legacy code from within generic code and using generic code within legacy code.

稍后,在转换遗留代码中使用泛型部分,我们将解决转换旧代码以使用泛型的问题。在本节中,我们将重点讨论一个更简单的问题:遗留代码和泛型代码如何互操作?这个问题有两个部分:从泛型代码中使用遗留代码,并在遗留代码中使用泛型代码。

Using Legacy Code in Generic Code-在泛型代码中使用非泛型

How can you use old code, while still enjoying the benefits of generics in your own code?

在您自己的代码中仍然享受泛型的好处时,您如何使用旧代码呢?

// As an example, assume you want to use the package com.Example.widgets.

// The folks at Example.com market a system for inventory control, highlights of which are shown below:

// 作为一个例子,假设您想要使用包com.example.widget

// 例如,Example.com 有一个库存控制系统,其中的高亮部分如下:

public interface Part {...} // 部件

// 库存

public class Inventory {

    /**

     * Adds a new Assembly to the inventory database.

     * The assembly is given the name name, and

     * consists of a set parts specified by parts.

     * All elements of the collection parts

     * must support the Part interface.

     * 向库存数据库添加一个新的组件。组件包括名字、若干指定部件的集合

     * 集合中的部件实现部件接口

     **/

    public static void addAssembly(String name, Collection parts) {...}

    public static Assembly getAssembly(String name) {...}

}

public interface Assembly { // 组件

    // Returns a collection of Parts

    // 返回部件的集合

    Collection getParts();

}

// Now, you'd like to add new code that uses the API above.

// It would be nice to ensure that you always called addAssembly() with the proper arguments - that is,

// that the collection you pass in is indeed a Collection of Part.

// Of course, generics are tailor made for this:

// 现在,您想要使用上面的API添加新的代码。

// 最好是,能继续使用旧有的部件集合做为参数来调用addAssembly(String name, Collection parts)接口。

// 当然,泛型是为这个量身定做的:

public class Blade implements Part { //

    ...

}

public class Guillotine implements Part { // 断头台

}

public class Main {

    public static void main(String[] args) {

        Collection<Part> c = new ArrayList<Part>();

        c.add(new Guillotine()) ;

        c.add(new Blade());

        Inventory.addAssembly("thingee", c); // 谋财害命 ^O^

        Collection<Part> k = Inventory.getAssembly("thingee").getParts();

    }

}

 

When we call addAssembly, it expects the second parameter to be of type Collection. The actual argument is of type Collection<Part>. This works, but why? After all, most collections don't contain Part objects, and so in general, the compiler has no way of knowing what kind of collection the type Collection refers to.

当我们调用addAssembly时,它期望第二个参数是Collection。实际的参数是Collection<Part>。它通过了,但是为什么呢?毕竟,大多数集合不包含Part对象,所以一般来说,编译器无法知道集合的类型指的是什么。

In proper generic code, Collection would always be accompanied by a type parameter. When a generic type like Collection is used without a type parameter, it's called a raw type.

在合理的泛型代码中,Collection总是伴随着一个类型参数。当没有类型参数的泛型(Collection)被使用时,它被称为原始类型

Most people's first instinct is that Collection really means Collection<Object>. However, as we saw earlier, it isn't safe to pass a Collection<Part> in a place where a Collection<Object> is required. It's more accurate to say that the type Collection denotes a collection of some unknown type, just like Collection<?>.

大多数人的第一反应是,Collection实际意味着Collection<Object>。然而,正如我们之前看到的,这是不安全的。在需要Collection<Object>的地方传递一个Collection<Part>。更准确的说法是,类型Collection表示某种未知类型的集合,就像Collection<?>

But wait, that can't be right either! Consider the call to getParts(), which returns a Collection. This is then assigned to k, which is a Collection<Part>. If the result of the call is a Collection<?>, the assignment would be an error.

但是等等,那也不对!请考虑getParts()的调用,其返回值为Collection。然后把它分配给k,这是一个Collection<Part>。函数的返回值shi Collection<?>,这个赋值将会报错。

In reality, the assignment is legal, but it generates an unchecked warning. The warning is needed, because the fact is that the compiler can't guarantee its correctness. We have no way of checking the legacy code in getAssembly() to ensure that indeed the collection being returned is a collection of Parts. The type used in the code is Collection, and one could legally insert all kinds of objects into such a collection.

实际上,k的这个赋值是合法的,但它会产生unchecked的警告。这个警告是必需的,因为事实是编译器不能保证它的正确性。我们没有办法检查getAssembly()中的遗留代码,以确保返回的Collection实际上是Part的集合。在代码中使用的类型是Collection,并且可以合法地将所有类型的对象插入到这样一个Collection中。

So, shouldn't this be an error? Theoretically speaking, yes; but practically speaking, if generic code is going to call legacy code, this has to be allowed. It's up to you, the programmer, to satisfy yourself that in this case, the assignment is safe because the contract of getAssembly() says it returns a collection of Parts, even though the type signature doesn't show this.

那么,这难道不是一个错误吗?理论上说,是的; 但实际上,如果泛型代码将调用遗留代码,则必须允许这样做。这取决于您,程序员,以满足您自己的要求,在这种情况下,赋值是安全的,因为getAssembly()的定义说它返回一个Parts的Collection,即使类型签名没有显示Part。

So raw types are very much like wildcard types, but they are not typechecked as stringently. This is a deliberate design decision, to allow generics to interoperate with pre-existing legacy code.

所以原始类型非常类似于通配符类型,但是它们并没有严格地进行类型检查。这是一个经过深思熟虑的设计决策,允许泛型与已有的遗留代码进行互操作。

Calling legacy code from generic code is inherently dangerous; once you mix generic code with non-generic legacy code, all the safety guarantees that the generic type system usually provides are void. However, you are still better off than you were without using generics at all. At least you know the code on your end is consistent.

从泛型代码中调用遗留代码本身是危险的; 一旦将泛型代码与非泛型代码混合在一起,泛型系统通常提供的所有安全性保证都是无效的。然而,您仍然比没有使用泛型的情况要好。至少你自己知道你的代码是一致的。

At the moment there's a lot more non-generic code out there then there is generic code, and there will inevitably be situations where they have to mix.

现在有更多的非泛型代码,然后是泛型代码,不可避免地会出现它们必须混合的情况。

If you find that you must intermix legacy and generic code, pay close attention to the unchecked warnings. Think carefully how you can justify the safety of the code that gives rise to the warning.

如果您发现必须混合遗产和通用代码,请密切关注未检查的警告。仔细考虑如何为引起警告,并保证代码的安全性。

What happens if you still made a mistake, and the code that caused a warning is indeed not type safe? Let's take a look at such a situation. In the process, we'll get some insight into the workings of the compiler.

如果您仍然犯了一个错误,导致警告的代码实际上不是类型安全的,会发生什么呢?让我们来看看这样的情况。在这个过程中,我们将深入了解编译器的工作原理。

Erasure and Translation-消除和转换

// 漏洞

public String loophole(Integer x) {

    List<String> ys = new LinkedList<String>();

    List xs = ys;

    xs.add(x); // Compile-time unchecked warning

    return ys.iterator().next();

}

// Here, we've aliased a list of strings and a plain old list.

// We insert an Integer into the list, and attempt to extract a String.

// This is clearly wrong. If we ignore the warning and try to execute this code,

// it will fail exactly at the point where we try to use the wrong type.

// At run time, this code behaves like:

// 在这里,我们给字符串List和一个简单的旧列表List。我们在列表中Insert一个整数,

// 并尝试读取一个字符串。这显然是错误的。如果我们忽略警告并尝试执行这段代码,

// 它将会在我们尝试使用错误类型的时候失败。在运行时,这段代码表现像如下代码:

public String loophole(Integer x) { // 漏洞

    List ys = new LinkedList;

    List xs = ys;

    xs.add(x);

    return(String) ys.iterator().next(); // run time error

}

// When we extract an element from the list,

// and attempt to treat it as a string by casting it to String,

// we will get a ClassCastException.

// The exact same thing happens with the generic version of loophole().

// 当我们从列表中提取一个元素,并尝试将其作为字符串进行处理时,

// 我们将得到ClassCastException。同样的事情也将发生在loophole()的泛型版本中。

 

The reason for this is, that generics are implemented by the Java compiler as a front-end conversion called erasure. You can (almost) think of it as a source-to-source translation, whereby the generic version of loophole()is converted to the non-generic version.

这样的原因是,泛型是由Java编译器实现的,作为一种称为擦除的前端转换。您可以(几乎)将其视为源到源的转换,从而将loophole()的泛型版本转换为非泛型版本。

As a result, the type safety and integrity of the Java virtual machine are never at risk, even in the presence of unchecked warnings.

因此,在unchecked 警告时,Java虚拟机的类型安全性和完整性永远不会受到威胁

Basically, erasure gets rid of (or erases) all generic type information. All the type information betweeen angle brackets is thrown out, so, for example, a parameterized type like List<String> is converted into List. All remaining uses of type variables are replaced by the upper bound of the type variable (usually Object). And, whenever the resulting code isn't type-correct, a cast to the appropriate type is inserted, as in the last line of loophole.

基本上,擦除消除(或擦除)所有泛型类型信息。所有尖括号内的类型信息都被剔除,例如,一个参数化类型如List<String>被转换为List。类型变量的类型,都被当作类型变量的上限(通常是Object)。而且,每当产生的代码不正确时,适当类型转换就会执行,就像在loophole方法的最后一行(return(String) ys.iterator().next();)

The full details of erasure are beyond the scope of this tutorial, but the simple description we just gave isn't far from the truth. It's good to know a bit about this, especially if you want to do more sophisticated things like converting existing APIs to use generics (see the Converting Legacy Code to Use Generics section), or just want to understand why things are the way they are.

擦除的全部细节超出了本教程的范围,但是我们刚刚给出的简单描述与事实并不遥远。了解这一点是很好的,特别是如果你想要做一些更复杂的事情比如将现有的api转换成使用泛型(参考Converting Legacy Code to Use Generics 一章),或者您只是想知道为什么事情是这样的。

Using Generic Code in Legacy Code-在泛型代码中使用非泛型

Now let's consider the inverse case. Imagine that Example.com chose to convert their API to use generics, but that some of their clients haven't yet. So now the code looks like:

现在我们来考虑一下相反的情况。想象一下,Example.com选择将他们的API转换为了使用泛型,但是他们的一些客户还没有转换使用泛型。现在代码看起来是这样:

// 在使用泛型的代码中使用非泛型

public interface Part {

    ...

}

public class Inventory {

    /**

     * Adds a new Assembly to the inventory database.

     * The assembly is given the name name, and

     * consists of a set parts specified by parts.

     * All elements of the collection parts

     * must support the Part interface.

     * 向库存数据库添加一个新的组件。组件包括名字、若干指定部件的集合

     * 集合中的部件实现部件接口

     **/

    public static void addAssembly(String name, Collection<Part> parts) {...}

    public static Assembly getAssembly(String name) {...}

}

public interface Assembly {

    // Returns a collection of Parts

    Collection<Part> getParts();

}

// and the client code looks like: 客户端代码:

public class Blade implements Part { // ^@^

...

}

public class Guillotine implements Part { // 断头台 -_-

}

public class Main {

    public static void main(String[] args) {

        Collection c = new ArrayList();

        c.add(new Guillotine()) ;

        c.add(new Blade());

 

        // 1: unchecked warning

        Inventory.addAssembly("thingee", c); // 谋财害命 ^o^

 

        Collection k = Inventory.getAssembly("thingee").getParts();

    }

}

// The client code was written before generics were introduced,

// but it uses the package com.Example.widgets and the collection library,

// both of which are using generic types.

// All the uses of generic type declarations in the client code are raw types.

// 客户端代码是在引入泛型之前编写的,但是它使用的是软件包com.example.widgets 和集合库,

// 它们都使用泛型。在客户端代码中,泛型类型声明的所有用法都是原始未使用泛型。

 

// Line 1 generates an unchecked warning,

// because a raw Collection is being passed in where a Collection of Parts is expected,

// and the compiler cannot ensure that the raw Collection really is a Collection of Parts.

// client代码的第1行生成一个未检查的警告,因为原始Collection正在被传递到一个Collection<Part>的地方,

// 而编译器不能确保原始集合实际上是一个Collection<Part>

 

// As an alternative, you can compile the client code using the source 1.4 flag,

// ensuring that no warnings are generated.

// However, in that case you won't be able to use any of the new language features introduced in JDK 5.0.

// 作为另一种选择,您可以使用源1.4来编译客户机代码,确保不会产生警告。

// 但是,在这种情况下,您将无法使用JDK 5.0中引入的任何新语言特性。

 

The Fine Print-附加条款

A Generic Class is Shared by All Its Invocations-泛型类由它所有的调用者共享

// What does the following code fragment print?

// 下面的代码片段打印什么?

List <String> l1 = new ArrayList<String>();

List<Integer> l2 = new ArrayList<Integer>();

System.out.println(l1.getClass() == l2.getClass());

// You might be tempted to say false, but you'd be wrong.

// It prints true, because all instances of a generic class have the same run-time class,

// regardless of their actual type parameters.

// 你可能会想说false,但你错了。它打印true

// 因为泛型类的所有实例都具有相同的运行时类,而不管它们的实际类型参数是什么

 

// Indeed, what makes a class generic is the fact that

// it has the same behavior for all of its possible type parameters;

// the same class can be viewed as having many different types.

// 实际上,使类泛化的原因是它对所有可能的类型参数都有相同的行为;

// 同一个类可以被看作有许多不同的类型。

 

As consequence, the static variables and methods of a class are also shared among all the instances. That is why it is illegal to refer to the type parameters of a type declaration in a static method or initializer, or in the declaration or initializer of a static variable.

因此,类的静态变量和方法也在所有实例中共享。这就是为什么在静态方法或初始化方法中的类型声明 或静态变量或初始化方法中的静态变量声明中 引用类型声明的类型参数是非法的。

Casts and InstanceOf-类型强转和对象类型判断

// Another implication of the fact that a generic class is shared among all its instances,

// is that it usually makes no sense to ask an instance

// if it is an instance of a particular invocation of a generic type:

// 泛型类在所有实例中共享的引发的另一个事实是,询问一个对象,它是否是特定泛型的实力,

// 通常是没有意义的:

Collection cs = new ArrayList<String>();

// Illegal. 非法的。

if (cs instanceof Collection<String>) { ... }

// similarly, a cast such as 类似地,像这样的强转:

// Unchecked warning, unchecked 警告;

Collection<String> cstr = (Collection<String>) cs;

// gives an unchecked warning,

// since this isn't something the runtime system is going to check for you.

// 这将给出一个未检查的警告,因为这不是运行时系统要检查的东西。

 

// The same is true of type variables

// 类型变量也是如此

// Unchecked warning.

<T> T badCast(T t, Object o) {

    return (T) o;

}

// Type variables don't exist at run time.

// This means that they entail no performance overhead in either time nor space,

// which is nice. Unfortunately, it also means that you can't reliably use them in casts.

// 类型变量在运行时不存在。这意味着它们在时间和空间中都不会产生任何性能开销,

// 这很好。不幸的是,这也意味着您不能在中可靠地对它们使用类型转换。

 

Arrays-数组

The component type of an array object may not be a type variable or a parameterized type, unless it is an (unbounded) wildcard type.You can declare array types whose element type is a type variable or a parameterized type, but not array objects.

数组的类型可能不是类型变量或参数化类型,除非它使用(无限制的)通配符类型。否则一个数组对象的元素不能是泛型

// This is annoying, to be sure. This restriction is necessary to avoid situations like:

// 当然,这很烦人。这一限制对于避免以下情况是必要的:

// Not really allowed.

List<String>[] lsa = new List<String>[10];

Object o = lsa;

Object[] oa = (Object[]) o;

List<Integer> li = new ArrayList<Integer>();

li.add(new Integer(3));

// Unsound, but passes run time store check,不健全,但能通过运行时存储检查

oa[1] = li;

 

// Run-time error: ClassCastException.

String s = lsa[1].get(0);

 

If arrays of parameterized type were allowed, the previous example would compile without any unchecked warnings, and yet fail at run-time. We've had type-safety as a primary design goal of generics. In particular, the language is designed to guarantee that if your entire application has been compiled without unchecked warnings using javac -source 1.5, it is type safe.

如果允许泛型数组的存在,上文的栗子将会编译通过并且不会包uncheck警告,但是到了运行时会报错。我们本着首要保证类型安全(type-safety)的泛型设计原则。特别的1.5保证在编译不报uncheck警告时它是类型安全的。

However, you can still use wildcard arrays. The following variation on the previous code forgoes the use of both array objects and array types whose element type is parameterized. As a result, we have to cast explicitly to get a String out of the array.

但是,您仍然可以使用通配符数组。针对前面的代码中,下面的代码是使用了参数化的数组对象和数组类型。我们必须显式地类型转换从数组中取出一个字符串。

// OK, array of unbounded wildcard type.

List<?>[] lsa = new List<?>[10];

Object o = lsa;

Object[] oa = (Object[]) o;

List<Integer> li = new ArrayList<Integer>();

li.add(new Integer(3));

// Correct.

oa[1] = li;

// Run time error, but cast is explicit.

// 根据通配符的定义以及Java类型擦除的保留上界原则,取出的将会是Object

String s = (String) lsa[1].get(0);

 

In the next variation, which causes a compile-time error, we refrain from creating an array object whose element type is parameterized, but still use an array type with a parameterized element type.

在接下来的变化中,我们避免创建一个参数化的数组对象这将会导致编译时错误,但是仍然使用带有参数化元素类型的数组类型。

// Error. 数组对象中有类型参数

List<String>[] lsa = new List<?>[10];

// Similarly, attempting to create an array object

// whose element type is a type variable causes a compile-time error:

// 类似地,试图创建一个数组对象,其元素类型是类型变量,这会导致编译时错误:

<T> T[] makeArray(T t) {

    return new T[100]; // Error.

}

 

Since type variables don't exist at run time, there is no way to determine what the actual array type would be.

因为类型变量在运行时不存在,所以没有办法确定实际的数组类型是什么。

The way to work around these kinds of limitations is to use class literals as run time type tokens, as described in the next section, Class Literals as Runtime-Type Tokens.

解决这类限制的方法是使用类的字面类型作为运行时类型,如下一节所述。

Class Literals as Runtime-Type Tokens-类的字面类型做为运行时类型

One of the changes in JDK 5.0 is that the class java.lang.Class is generic. It's an interesting example of using genericity for something other than a container class.

JDK 5.0中的一个变化是,类java.lang.Classs是泛型的。这是一个很有趣的例子,它使用泛型,而不是容器类。

Now that Class has a type parameter T, you might well ask, what does T stand for? It stands for the type that the Class object is representing.

现在这个类有一个类型参数T,你可能会问,T代表什么?它代表类对象所表示的类型。

For example, the type of String.class is Class<String>, and the type of Serializable.class is Class<Serializable>. This can be used to improve the type safety of your reflection code.

例如,String.class 的类型是Class<String>Serializable.class 的类型是Class<Serializable>,这可以用来提高反射代码的类型安全性。

In particular, since the newInstance() method in Class now returns a T, you can get more precise types when creating objects reflectively.

特别地,由于类中的newInstance() 方法现在返回一个T,您可以在反射时获得更精确的类型。

For example, suppose you need to write a utility method that performs a database query, given as a string of SQL, and returns a collection of objects in the database that match that query.

栗如,假设您需要编写一个实用程序方法来执行数据库查询,作为一连串的SQL,并返回与该查询相匹配的数据库中的对象集合。

// One way is to pass in a factory object explicitly, writing code like:

// 一种方法是显式地传递工厂对象,这样编码:

interface Factory<T> { T make();}

 

public <T> Collection<T> select(Factory<T> factory, String statement) {

    Collection<T> result = new ArrayList<T>();

 

    /* Run sql query using jdbc */ 

    for (/* Iterate over jdbc results. */) {

        T item = factory.make();

        /* Use reflection and set all of item's

         * fields from sql results.

         */

        result.add(item);

    }

    return result;

}

// You can call this either as

// 您可以这样调用

select(new Factory<EmpInfo>(){

    public EmpInfo make() {

        return new EmpInfo();

    }}, "selection string");

// or you can declare a class EmpInfoFactory to support the Factory interface

// 或者你可以声明一个EmpInfoFactory类来支持工厂接口

class EmpInfoFactory implements Factory<EmpInfo> {

    ...

    public EmpInfo make() {

        return new EmpInfo();

    }

}

// and call it 这样调用

select(getMyEmpInfoFactory(), "selection string");

 

The downside of this solution is that it requires either:

  1. the use of verbose anonymous factory classes at the call site, or
  2. declaring a factory class for every type used and passing a factory instance at the call site, which is somewhat unnatural.

这个解决方案的缺点是它需要:

  1. 在调用时使用冗长的匿名工厂类,或者
  2. 为所使用的每一种类型声明一个工厂类,并在调用时传递一个工厂实例,这有点不自然。

It is natural to use the class literal as a factory object, which can then be used by reflection. Today (without generics) the code might be written:

使用类的字面类型作为工厂对象是很自然的,可以通过反射来使用它。今天(没有泛型)代码可能会这样写:

// 可以通过反射来使用它。

Collection emps = sqlUtility.select(EmpInfo.class, "select * from emps");

...

public static Collection select(Class c, String sqlStatement) {

    Collection result = new ArrayList();

    /* Run sql query using jdbc. */

    for (/* Iterate over jdbc results. */ ) {

        Object item = c.newInstance();

        /* Use reflection and set all of item's

         * fields from sql results.

         */ 

        result.add(item);

    }

    return result;

}

// However, this would not give us a collection of the precise type we desire.

// Now that Class is generic, we can instead write the following:

// 然而,这并不能给我们提供我们想要的精确类型的集合。

// 现在Class是泛型的,我们可以这样写:

Collection<EmpInfo> emps = sqlUtility.select(EmpInfo.class, "select * from emps");

...

public static <T> Collection<T> select(Class<T> c, String sqlStatement) {

    Collection<T> result = new ArrayList<T>();

    /* Run sql query using jdbc. */

    for (/* Iterate over jdbc results. */ ) {

        T item = c.newInstance();

        /* Use reflection and set all of item's

         * fields from sql results.

         */ 

        result.add(item);

    }

    return result;

}

// The above code gives us the precise type of collection in a type safe way.

// 上面的代码以一种安全的方式提供了精确的集合类型。

 

This technique of using class literals as run time type tokens is a very useful trick to know. It's an idiom that's used extensively in the new APIs for manipulating annotations, for example.

这种使用类字面类型作为运行时类型的技术是一个非常有用的技巧。例如,在用于操作注解的新api中,这是一个广泛使用的方式。

More Fun with Wildcards-通配符的更多用法

In this section, we'll consider some of the more advanced uses of wildcards. We've seen several examples where bounded wildcards were useful when reading from a data structure. Now consider the inverse, a write-only data structure. The interface Sink is a simple example of this sort.

在本节中,我们将考虑一些更高级的通配符用法。我们已经看到了几个例子,其中有界通配符在从数据结构中读取时非常有用。现在考虑一下相反的情况,一个只写的数据结构。

Sink接口就是这样一个简单的例子。

// 下沉接口栗子

interface Sink<T> {

    flush(T t);

}

// We can imagine using it as demonstrated by the code below.

// The method writeAll() is designed to

// flush all elements of the collection coll to the sink snk, and return the last element flushed.

// 我们可以想象使用下面的代码来演示它。

// 方法writeAll() 被设计成将收集coll的所有元素刷新到sink nk,并返回最后一个元素。

public static <T> T writeAll(Collection<T> coll, Sink<T> snk) {

    T last;

    for (T t : coll) {

        last = t;

        snk.flush(last);

    }

    return last;

}

...

Sink<Object> s;

Collection<String> cs;

String str = writeAll(cs, s); // Illegal call.

// As written, the call to writeAll() is illegal,

// as no valid type argument can be inferred;

// neither String nor Object are appropriate types for T,

// because the Collection element and the Sink element must be of the same type.

// 正如所写的那样,对writeAll()的调用是非法的,因为没有有效的类型参数可以推断;

// StringObject都不是T的合适类型,因为集合元素和Sink元素必须是相同类型的。

 

// We can fix this error by modifying the signature of writeAll() as shown below, using a wildcard.

// 我们可以通过修改writeAll()的签名来修复这个错误,如下所示,使用通配符。

public static <T> T writeAll(Collection<? extends T>, Sink<T>) {...}

...

// Call is OK, but wrong return type.

String str = writeAll(cs, s);

// The call is now legal, but the assignment is erroneous,

// since the return type inferred is Object because T matches the element type of s, which is Object.

// 这个调用现在是合法的,但是赋值是错误的,因为返回类型推断为对象,因为Ts的元素类型将推断为Object

 

wildcards with a lower bound.

带下界的通配符

// The solution is to use a form of bounded wildcard we haven't seen yet:

// wildcards with a lower bound. The syntax ? super T denotes an unknown type

// that is a supertype of T (or T itself; remember that the supertype relation is reflexive).

// It is the dual of the bounded wildcards we've been using,

// where we use ? extends T to denote an unknown type that is a subtype of T.

// 解决方案是使用一种我们还没有看到的有界通配符的形式:带有下限的通配符。

// 语法 ? super T表示一种未知类型,它是T的超类型(或T本身;记住,超类型关系是反射性的)。

// 它是我们一直使用的有界通配符的另一种形式,我们使用的是 extend T表示一个未知类型,它是T的子类型。

public static <T> T writeAll(Collection<T> coll, Sink<? super T> snk) {

    ...

}

String str = writeAll(cs, s); // Yes!

// Using this syntax, the call is legal, and the inferred type is String, as desired.

// 使用这种语法,调用是合法的,推断出的类型是字符串,正如所期望的那样。

 

// Now let's turn to a more realistic example.

// A java.util.TreeSet<E> represents a tree of elements of type E that are ordered.

// One way to construct a TreeSet is to pass a Comparator object to the constructor.

// That comparator will be used to sort the elements of the TreeSet according to a desired ordering.

// 现在让我们来看一个更现实的例子。java.utilTreeSet表示订购的E类型的元素树。

// 构建TreeSet的一种方法是将Comparator对象传递给构造函数。

// 这个比较器将被用来根据需要的顺序对TreeSet的元素进行排序。

TreeSet(Comparator<E> c)

// The Comparator interface is essentially:

// 比较器接口的本质是:

interface Comparator<T> {

    int compare(T fst, T snd);

}

// Suppose we want to create a TreeSet<String> and pass in a suitable comparator,

// We need to pass it a Comparator that can compare Strings.

// This can be done by a Comparator<String>, but a Comparator<Object> will do just as well.

// However, we won't be able to invoke the constructor given above on a Comparator<Object>.

// We can use a lower bounded wildcard to get the flexibility we want:

// 假设我们想要创建一个TreeSet并传入一个合适的比较器,我们需要传递一个可以比较字符串的比较器。

// 这可以由Comparator<String>来完成,但是Comparator<Object>也可以做同样的事情。

// 然而,我们将无法调用上面给出的Comparator<Object>。我们可以使用一个下界通配符来获得我们想要的灵活性:

TreeSet(Comparator<? super E> c)

// This code allows any applicable comparator to be used.

// 这段代码允许使用任何适用的比较器。

 

As a final example of using lower bounded wildcards, lets look at the method Collections.max(), which returns the maximal element in a collection passed to it as an argument. Now, in order for max() to work, all elements of the collection being passed in must implement Comparable. Furthermore, they must all be comparable to each other.

作为使用低界通配符的最后一个例子,让我们看看方法collections.max(),它返回传递给它作为参数的集合中的最大元素。现在,为了让max()工作,被传递的集合的所有元素都必须实现可比较的。此外,它们必须彼此相比较。

// A first attempt at generifying this method signature yields:

// 第一次尝试泛化这种方法签名的结果是:

public static <T extends Comparable<T>> T max(Collection<T> coll)

// That is, the method takes a collection of some type T that is comparable to itself,

// and returns an element of that type.

// However, this code turns out to be too restrictive.

// To see why, consider a type that is comparable to arbitrary objects:

// 也就是说,该方法接受某种类型T的集合,它可以与自身相比较,并返回该类型的一个元素。

// 然而,这段代码被证明是过于严格的。要知道为什么,考虑一种可与任意对象相媲美的类型:

class Foo implements Comparable<Object> {

    ...

}

Collection<Foo> cf = ... ;

Collections.max(cf); // Should work.

// Every element of cf is comparable to every other element in cf, since every such element is a Foo,

// which is comparable to any object, and in particular to another Foo.

// However, using the signature above, we find that the call is rejected.

// The inferred type must be Foo, but Foo does not implement Comparable<Foo>.

// cf的每一个元素都可以与cf中的其他元素相比较,因为每个这样的元素都是Foo,它可以与任何对象相比较,

// 特别是对于另一个Foo。但是,使用上面的签名,

// 我们发现调用被拒绝了。推断出的类型必须是Foo,但是Foo并没有实现可比较的Foo

 

// It isn't necessary that T be comparable to exactly itself.

// All that's required is that T be comparable to one of its supertypes. This give us:

// 它不需要与它本身相比较。所需要的就是T与它的一种超级类型相比较。这给我们提示:

public static <T extends Comparable<? super T>>

        T max(Collection<T> coll)

// Note that the actual signature of Collections.max() is more involved.

// We return to it in the next section, Converting Legacy Code to Use Generics.

// This reasoning applies to almost any usage of Comparable that is intended to work for arbitrary types:

// You always want to use Comparable<? super T>.

// 请注意,collections.max() 的实际签名更加复杂。我们在下一节中重新讨论它,

// 将遗留代码转换为使用泛型。这种推理适用于几乎任何可用于任意类型的可比较的用法:您总是偏向使用Comparable<super T >

 

In general, if you have an API that only uses a type parameter T as an argument, its uses should take advantage of lower bounded wildcards (super T). Conversely, if the API only returns T, you'll give your clients more flexibility by using upper bounded wildcards (extends T).

一般来说,如果你有一个只使用类型参数T作为参数的API,它的使用应该利用下有界通配符(?super T)。相反,如果API只返回T,那么您将通过使用上界通配符(扩展T)来为代码提供更大的灵活性。

Wildcard Capture-通配符匹配

// It should be pretty clear by now that given:

// 现在看到以下代码应该已经很清楚了:

Set<?> unknownSet = new HashSet<String>();

...

/* Add an element  t to a Set s. */

public static <T> void addToSet(Set<T> s, T t) {

    ...

}

// The call below is illegal.

// 下面的调用是非法的。

addToSet(unknownSet, "abc"); // Illegal.

 

// It makes no difference that the actual set being passed is a set of strings;

// what matters is that the expression being passed as an argument is a set of an unknown type,

// which cannot be guaranteed to be a set of strings, or of any type in particular.

// 传递的实际集合是一组字符串,这是没有区别的;重要的是,作为参数传递的表达式是一组未知类型,

// 不能保证它是一组字符串,或者特定类型的字符串。

 

// Now, consider the following code:

// 现在,考虑以下代码:

Set<?> unknownSet = new HashSet<String>();

...

class Collections {

    ...

    <T> public static Set<T> unmodifiableSet(Set<T> set) {

        ...

    }

}

...

Set<?> s = Collections.unmodifiableSet(unknownSet); // This works! Why?

// It seems this should not be allowed; yet, looking at this specific call, it is perfectly safe to permit it.

// After all, unmodifiableSet() does work for any kind of Set, regardless of its element type.

// 这似乎是不允许的;然而,看看这个具体的调用,允许它是完全安全的。

// 毕竟,unmodifiableSet()对于任何类型的集合都是有效的,不管它的元素类型是什么。

 

Because this situation arises relatively frequently, there is a special rule that allows such code under very specific circumstances in which the code can be proven to be safe. This rule, known as wildcard capture, allows the compiler to infer the unknown type of a wildcard as a type argument to a generic method.

由于这种情况相对频繁地出现,所以有一个特殊的规则,允许在非常特定的情况下,代码可以被证明是安全的。这个规则,称为通配符捕获,允许编译器将未知类型的通配符作为泛型方法的类型参数推断出来。

Converting Legacy Code to Use Generics

Earlier, we showed how new and legacy code can interoperate. Now, it's time to look at the harder problem of "generifying" old code.

早些时候,我们展示了泛型的和遗留的代码是如何互操作的。现在,是时候看看更难解决的问题了泛化旧代码。

If you decide to convert old code to use generics, you need to think carefully about how you modify the API.

如果您决定将旧代码转换为使用泛型,那么您需要仔细考虑如何修改API

You need to make certain that the generic API is not unduly restrictive; it must continue to support the original contract of the API. Consider again some examples from java.util.Collection. The pre-generic API looks like:

您需要确保泛化API没有过度限制;它必须继续支持API的原始约束。再来看看java.util.collection中的一些例子。通用的API看起来是这样的:

// 通用的API看起来是这样的:

interface Collection {

    public boolean containsAll(Collection c);

    public boolean addAll(Collection c);

}

// A naive attempt to generify it would be the following:

// 对其进行泛化的一种天真的尝试是:

interface Collection<E> {

 

    public boolean containsAll(Collection<E> c);

    public boolean addAll(Collection<E> c);

}

 

While this is certainly type safe, it doesn't live up to the API's original contract. The containsAll() method works with any kind of incoming collection. It will only succeed if the incoming collection really contains only instances of E, but:

  1. The static type of the incoming collection might differ, perhaps because the caller doesn't know the precise type of the collection being passed in, or perhaps because it is a Collection<S>,where S is a subtype of E.
  2. It's perfectly legitimate to call containsAll() with a collection of a different type. The routine should work, returning false.

虽然这肯定是安全的,但它并没有达到API的原始约束。containsAll() 方法对任何类型的传入集合生效。以上的泛化只有在传入的集合真正包含E的实例时才会成功,但是:

  1. 传入的集合的静态类型可能有所不同,可能是因为调用者不知道传入的集合的精确类型,或者可能是因为它是一个Collection<S>,Se的子类型。
  2. 用和E不同类型的集合调用containsAll()是完全合法的。例程应该工作,返回false

In the case of addAll(), we should be able to add any collection that consists of instances of a subtype of E. We saw how to handle this situation correctly in section Generic Methods.

addAll() 的例子中,我们应该能够添加任何包含E子类型实例的集合,我们已经学会了如何正确地处理这种情况,在泛型方法一节。

You also need to ensure that the revised API retains binary compatibility with old clients. This implies that the erasure of the API must be the same as the original, ungenerified API. In most cases, this falls out naturally, but there are some subtle cases. We'll examine one of the subtlest cases we've encountered, the method Collections.max(). As we saw in section More Fun with Wildcards, a plausible signature for max() is:

您还需要确保修改后的API与旧客户端保持代码兼容性。这意味着对API的擦除必须与原始的、非泛化的API相同。在大多数情况下,这是自然发生的,但也有一些微妙的情况。我们将研究我们遇到的最不重要的案例之一,方法Collection.max()。正如我们在通配符的乐趣章节中看到的,max() 的一个看似合理的定义是:

// 方法Collection.max()。的一个看似合理的定义是:

public static <T extends Comparable<? super T>>

        T max(Collection<T> coll)

// This is fine, except that the erasure of this signature is:

// 这很好,擦除泛型后方法定义为:

public static Comparable max(Collection coll)

// which is different than the original signature of max():

// 这与max() 的原始签名不同:

public static Object max(Collection coll)

// One could certainly have specified this signature for max(), but it was not done,

// and all the old binary class files that call Collections.max() depend on a signature that returns Object.

// 当然,我们可以为max() 指定这个定义,但是它没有完成,

// 并且所有调用Collections.max()的旧代码类文件都依赖于返回Object的定义。

// We can force the erasure to be different,

// by explicitly specifying a superclass in the bound for the formal type parameter T.

// 我们可以通过显式地为正式类型参数t指定一个超类,从而改变擦除后的类型。

public static <T extends Object & Comparable<? super T>>

        T max(Collection<T> coll)

// This is an example of giving multiple bounds for a type parameter,

// using the syntax T1 & T2 ... & Tn. A type variable with multiple bounds

// is known to be a subtype of all of the types listed in the bound.

// When a multiple bound is used, the first type mentioned in the bound is used as the erasure of the type variable.

// 这是一个为类型参数提供多个界限的例子,使用语法T1T2&Tn

// 一个具有多个边界的类型变量被认为是绑定中列出的所有类型的子类型。当使用多重绑定时,

// 在绑定中提到的第一种类型被用作类型擦除后的变量。

 

Finally, we should recall that max only reads from its input collection, and so is applicable to collections of any subtype of T.

最后,我们应该记住,max只从它的输入集合中读取,因此适用于任何类型t的子类型的集合。

// This brings us to the actual signature used in the JDK:

// 这就引出了JDK中使用的实际签名:

public static <T extends Object & Comparable<? super T>>

        T max(Collection<? extends T> coll)

// It's very rare that anything so involved comes up in practice,

// but expert library designers should be prepared to

// think very carefully when converting existing APIs.

// 在实践中涉及到的这样的情形是非常罕见的,

// 但是专家库的设计者在转换现有的api时应该准备好应对各种情况。

 

Another issue to watch out for is covariant returns, that is, refining the return type of a method in a subclass. You should not take advantage of this feature in an old API. To see why, let's look at an example.

另一个需要注意的问题是可变返回,也就是说,在子类中细化方法的返回类型。您不应该在旧的API中利用这个特性。为了了解原因,让我们来看一个例子。

// Assume your original API was of the form:

// 假设您最初的API是这样的:

public class Foo {

    // Factory. Should create an instance of

    // whatever class it is declared in.

    public Foo create() {

        ...

    }

}

 

public class Bar extends Foo {

    // Actually creates a Bar.

    public Foo create() {

        ...

    }

}

// Taking advantage of covariant returns, you modify it to:

// 利用可变返回值,您将其修改为:

public class Foo {

    // Factory. Should create an instance of

    // whatever class it is declared in.

    public Foo create() {

        ...

    }

}

 

public class Bar extends Foo {

    // Actually creates a Bar.

    public Bar create() {

        ...

    }

}

// Now, assume a third party client of your code wrote the following:

// 现在,假设您的代码的第三方客户端写了以下内容:

public class Baz extends Bar {

    // Actually creates a Baz.

    public Foo create() {

        ...

    }

}

// The Java virtual machine does not directly support overriding of methods with different return types.

// This feature is supported by the compiler.

// Consequently, unless the class Baz is recompiled, it will not properly override the create() method of Bar.

// Furthermore, Baz will have to be modified,

// since the code will be rejected as written--the return type of create()

// in Baz is not a subtype of the return type of create() in Bar.

// Java虚拟机并不直接支持具有不同返回类型的方法的覆盖。该特性由编译器支持。因此,除非类Baz被重新编译,

// 否则不会正确地覆盖Barcreate() 方法。因此,必须对Baz进行修改,因为代码将会被拒绝,

// 因为在Bar中,create() 的返回类型不是在Barcreate() 返回类型的子类型。

 

Acknowledgements-致谢

Erik Ernst, Christian Plesner Hansen, Jeff Norton, Mads Torgersen, Peter von der Ahe and Philip Wadler contributed material to this trail .

Thanks to David Biesack, Bruce Chapman, David Flanagan, Neal Gafter, Orjan Petersson, Scott Seligman, Yoshiki Shibata and Kresten Krab Thorup for valuable feedback on earlier versions of this trail. Apologies to anyone I've forgotten.

翻译仅供参考,如有不对之处,自个看英文啊^O^

 

猜你喜欢

转载自blog.csdn.net/u012296499/article/details/81844583