Generic Types
本文最后更新于 2024年3月19日 上午
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.
A Simple Box Class
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:
1 |
|
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.
A Generic Version of the Box Class
泛型类按以下格式定义:
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.
将泛型参数应用于 Box
类:
1 |
|
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.
同样的技术可用于创建通用接口。
Type Parameter Naming Conventions
按照约定,类型参数名称是单个大写字母。
最常用的类型参数名称是:
- E - Element (used extensively by the Java CollectionsFramework)
- E - Element
- K - Key
- N - Number
- T - Type
- V - Value
Invoking and Instantiating a Generic Type
要从代码中引用泛型 Box
类,必须执行泛型类型调用,它将 T
替换为一些具体值,例如 Integer
:
Box<Integer> integerBox;
可以将泛型类型调用视为与普通方法调用类似,但不是将参数传递给方法,而是将类型参数(在本例中为 Integer )传递给 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<T>
is a type parameter and the String
in Foo<String> f
is a type argument.
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<Integer>
is read.
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 <Integer>
between the class name and the parenthesis:
1 |
|
The Diamond
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<Integer>
with the following statement:
1 |
|
Multiple Type Parameters
如前所述,泛型类可以有多个类型参数(type parameters)。例如,通用 OrderedPair
类,它实现通用 Pair
接口:
1 |
|
以下语句创建 OrderedPair
类的两个实例:
1 |
|
代码 new OrderedPair<String, Integer>
将 K
实例化为 String
,将 V
实例化为 Integer
。因此,OrderedPair
构造函数的参数类型分别为 String
和 Integer
。
要创建通用接口,遵循与创建通用类相同的约定。
Parameterized Types
You can also substitute a type parameter (that is, K
or V
) with a parameterized type (that is, List<String>
). For example, using the OrderedPair<K, V>
example:
1 |
|
Raw Types
A raw type is the name of a generic class or interface without any type arguments. For example, given the generic Box class:
1 |
|
To create a parameterized type of Box<T>
, you supply an actual type argument for the formal type parameter T
:
1 |
|
If the actual type argument is omitted, you create a raw type of Box<T>
:
1 |
|
Therefore, Box is the raw type of the generic type Box<T>
. However, a non-generic class or interface type is not a raw type.
Raw types show up in legacy code because lots of API classes (such as the Collections classes) were not generic prior to JDK 5.0. When using raw types, you essentially get pre-generics behavior — a Box
gives you Objects. For backward compatibility, assigning a parameterized type to its raw type is allowed:
1 |
|
But if you assign a raw type to a parameterized type, you get a warning:
1 |
|
You also get a warning if you use a raw type to invoke generic methods defined in the corresponding generic type:
1 |
|
The warning shows that raw types bypass generic type checks, deferring the catch of unsafe code to runtime. Therefore, you should avoid using raw types.