原文地址:https://docs.oracle.com/javase/tutorial/java/generics/types.html
class name
Foo
中的T为类型参数Foo
中的String为类型变量A generic type is a generic class or interface that is parameterized over types. The following Box class will be modified to demonstrate the concept.
一个泛型类型是一个以类型为参数的泛型类或接口。下面的这个Box类将会被改写以说明问题。
Begin by examining a non-generic Box class that operates on objects of any type. It needs only to provide two methods: set, which adds an object to the box, and get, which retrieves it:
首先以一个非泛型BOX类做实验。这个类对任何类型的对象操作。它需要提供两个方法:
public class Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
Since its methods accept or return an Object, you are free to pass in whatever you want, provided that it is not one of the primitive types. There is no way to verify, at compile time, how the class is used. One part of the code may place an Integer in the box and expect to get Integers out of it, while another part of the code may mistakenly pass in a String, resulting in a runtime error.
因为它的方法接受并返回一个Object实例,所以你可以传入任何你想要的东西,前提是它不是原始类型。在编译时,无法确认这个类是如何被使用的。你代码中的一部分也许希望向box中添加一个整型,并期望取出的也是整型。然而另一部分的代码也许会错误的传入一个String,这样就会导致一个运行时错误。
A generic class is defined with the following format:
一个泛型类以下面的格式进行定义:
class name<T1, T2, ..., Tn> { /* ... */ }
The type parameter section, delimited by angle brackets (<>), follows the class name. It specifies the type parameters (also called type variables) T1, T2, …, and Tn.
由尖括号分开的类型参数的部分,它位于类名的后面。它指明了类型参数(亦称为类型变量)T1,T2,…,以及Tn。
To update the Box class to use generics, you create a generic type declaration by changing the code “public class Box” to “public class Box”. This introduces the type variable, T, that can be used anywhere inside the class.
为了使用泛型更新Box类,你需要把代码public class Box
更改为public class Box
以创建一个泛型类型的声明。这里我们引入类型变量T,你可以在类内部任何地方使用它。
With this change, the Box class becomes:
根据这项改动,Box类变成了这样:
/**
* Generic version of the Box class.
* @param the type of the value being boxed
*/
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
As you can see, all occurrences of Object are replaced by T. A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.
正如你所见,原来Object的位置都替换为了T。类型变量可以是你指定的任何非原始类型:任何类类型,任何接口类型,任何数组类型,甚至亦可为另一个类型变量。
This same technique can be applied to create generic interfaces.
可用同样的技术创建泛型接口
By convention, type parameter names are single, uppercase letters. This stands in sharp contrast to the variable naming conventions that you already know about, and with good reason: Without this convention, it would be difficult to tell the difference between a type variable and an ordinary class or interface name.
按惯例,类型参数由单一的大写字母命名。这与你所知的变量命名传统形成了强烈的对比,这样的对比会带来很多好处:如果没有类型参数自己的命名传统,区分一个类型变量和一个常规的类或接口名字就会变得十分困难。
The most commonly used type parameter names are:
- E - Element (used extensively by the Java Collections Framework)
- K - Key
- N - Number
- T - Type
- V - Value
- S,U,V etc. - 2nd, 3rd, 4th types
最常见的类型变量名有:
You’ll see these names used throughout the Java SE API and the rest of this lesson.
你会看到这些名字的使用贯穿于整个JavaSE API和本课时的剩余部分。
To reference the generic Box class from within your code, you must perform a generic type invocation, which replaces T with some concrete value, such as Integer:
为了在你代码中引用这个泛型Box类,你必须创建一个泛型类型,并将T替换为某些具体的值,如Integer。
Box<Integer> integerBox;
You can think of a generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you are passing a type argument — Integer in this case — to the Box class itself.
你可以将泛型的调用同常规方法调用做类比,但是并非向一个方法中传入变量,在本例中,你会传入一个类型参数——Interger到这个Box类本身
Type Parameter and Type Argument Terminology: Many developers use the terms “type parameter” and “type argument” interchangeably, but these terms are not the same. When coding, one provides type arguments in order to create a parameterized type. Therefore, the T in
Foo
is a type parameter and the String inFoo
f is a type argument. This lesson observes this definition when using these terms.
术语:“类型参数”与“类型变量”:很多开发者将两条术语混为一谈,但实际上他们并不相同。编写代码时,人们会提供类型参数以创建一个参数化的类型。因此,Foo
中的T就是一个类型参数,Foo
中的String则为类型变量。本课时在使用这两条术语时将遵循它们的定义。
Like any other variable declaration, this code does not actually create a new Box object. It simply declares that integerBox will hold a reference to a “Box of Integer”, which is how Box is read.
如同其他变量的声明,这段代码并不实际创建一个新的Box对象。它只是简单的声明了integerBox变量会持有一个对“整型类型的Box”的引用。而这就是Box
所传达的意思。
An invocation of a generic type is generally known as a parameterized type.
调用的泛型类型也被人们广泛地称为一种参数化类型
To instantiate this class, use the new keyword, as usual, but place
between the class name and the parenthesis:
为了实例化这个类,我们会同往常一样使用new关键字,但会把
放在类名和圆括号的中间:
Box
In Java SE 7 and later, you can replace the type arguments required to invoke the constructor of a generic class with an empty set of type arguments (<>) as long as the compiler can determine, or infer, the type arguments from the context. This pair of angle brackets, <>, is informally called the diamond. For example, you can create an instance of Box with the following statement:
在JavaSE 7 或者更新版本中,你可以将需要的类型变量进行替换以调用泛型类的构造器。替换的形式为“<>”,它是一个类型变量的空集合。编译器可以从上下文中决定,或者推出类型变量。这对尖括号,<>,大家喜欢称它为钻石运算符。例如,你可以通过下面的声明创建一个Box实例。
Box
For more information on diamond notation and type inference, see Type Inference.
关于钻石运算符和类型推断的更多信息,参见《类型推测》
As mentioned previously, a generic class can have multiple type parameters. For example, the generic OrderedPair class, which implements the generic Pair interface:
如上所述,一个泛型类可以具有多个类型参数。例如,泛型类OrderedPair就实现了泛型接口Pair
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
The following statements create two instantiations of the OrderedPair class:
下面的声明创建了OrderedPair类的两种实例。
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");
The code, new OrderedPair
, instantiates K as a String and V as an Integer. Therefore, the parameter types of OrderedPair’s constructor are String and Integer, respectively. Due to autoboxing, it is valid to pass a String and an int to the class.
代码new OrderedPair
将K实例化为String,V实例化为一个Integer。因此,此类构造器的参数类型就分别为是String和Integer。根据自动置入置出,传入一个String和int也是有效的。
As mentioned in The Diamond, because a Java compiler can infer the K and V types from the declaration OrderedPair
, these statements can be shortened using diamond notation:
正如《钻石运算符》一小节所提到的,因为Java编译器可以从OrderedPair
声明中推测出K和V的具体类型,这些声明可以通过钻石运算符的使用而缩短:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
To create a generic interface, follow the same conventions as for creating a generic class.
为了创建泛型接口,只需要遵循创建泛型类同样的规则就行了。
You can also substitute a type parameter (i.e., K or V) with a parameterized type (i.e., List). For example, using the OrderedPair
example:
你也可以将一根类型参数(如K,V)取代为一个参数化的类型(如List
)。例如这个OrderedPair
的例子:
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));
下一节:Raw Types