Generic Types
generic types
泛型类型指的是通过类型进行参数化的通用类或者接口。下面通过修改Box
类来掩饰这一概念。
A Simple Box Class
首先我们来看通过使用对象来操作任意类型的非泛型Box
类。它只需要两个函数:set
向box
中添加对象和get
从box
中获取对象。
因为box
的函数接受或者返回一个对象,所以只要不使用原始类型,你可以任意的传递你想要的内容。同时在贬义的时候没办法验证类被如何使用。在一部分代码中可能将一个Integer
添加到box
中并希望取出IntegerS
,但是在另外的一部分代码中错误的传入了一个String
,这样会导致runtime error
(运行时报错)
A Generic Version of the Box Class
generic class
(泛型类)定义格式如下:
类名后面使用尖括号(<>)分隔出类型参数部分。它指定type parameters
(类型参数,也叫type variables
-类型变量)T1,T2, ...,Tn
对上面的Box
类使用泛型,将代码从public class Box
改成public class Box<T>
来创建一个generic type declaration
(泛型类型声明)。这里引入可以在类内部任何地方使用的类型变量T
。
新的Box
类如下所示:
如你所见所有出现Object
的地方被替换为T
。一个类型变量可是你指定的任何non-primitive type
(非原始类型):任意的类类型,任意的接口类型,任意的数组类型,甚至可以是其他的类型变量。
我们可以使用同样的方式创建泛型接口。
Type Parameter Naming Conventions
按照惯例,类型参数的命名使用单个大写字母。这种方式和你已知的变量命名规约有着鲜明的对比,但是这是有原因的:没有这个惯例,那么将很难区分一个类型变量和一个普通的类或者接口名。
经常被使用的类型变量名如下:
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
你可以在Java SE API
和接下来的课程中找到这些命名方式。
Invoking and Instantiating a Generic Type
在你的代码中引用泛型Box
类型,你必须用到generic type invocation
(泛型类型调用),也就是将T
替换为具体的值,比如Integer
:
你可以认为泛型类型调用和普通的函数调用类似,不同的是函数调用是传入参数,而泛型类型调用是传一个类型参数-这个例子中是传Integer到Box类。
Type Parameter and Type Argument Terminology:许多开发者会混淆术语
type parameter
(类型参数)和type argument
(类型传参),但是它们是不同的。在编程时,提供type argument
是为了创建一个参数化的类型,因此Foo<T>
中的T
就是type parameter
,而Foo<String> f
中的String
就是type argument
,本文遵循此定义。
与其他的变量声明一样,这段代码不会真的创建一个新的Box
对象。它只是声明integerBox
持有一个Box of Integer
的引用,这就是读取Box<Integer>
的方式。
调用泛型类型也被称为parameterized type
(参数化类型)
为了实例化对象,我们通常使用new
关键字,在类名和圆括号之间添加<Integer>
:
The Diamond
在Java SE 7
和以后版本,只要编译器能从上下文中确定或者推断出类型参数,就可以使用一组空的类型参数<>
来替换掉用泛型类的构造函数所需的类型参数。这对尖括号<>
,被称为the diamond
(菱形运算符)。例如,你可以用下面这条语句创建一个Box<Integer>
实例:
更多的菱形元算符信息请参考Type Inference
Multiple Type Parameters
上面提到过,一个泛型类可以有多个类型参数(parameters)。例如,实现Pair
泛型接口的OrderedPair
泛型类:
下面的语句创建了两个OrderedPair
类的实例:
代码new OrderedPair<String, Integer>
将K
实例化为String
,将V
实例化为Integer
。所以OrderedPair
构造函数的参数类型分别是是String
和Integer
。根据autoboxing,传入String
和int
也是有效的。
根据上面提到的菱形运算符,由于Java编译器可以通过OrderedPair<String, Integer>
的声明推断出K
和V
的类型,所以上面的语句可以通过菱形运算符缩短成:
和创建泛型类一样使用相同的规则创建泛型接口。
Parameterized Types
你也可以将一个类型参数 (i.e., K or V) 替换为参数化类型(i.e., List)。拿OrderedPair<K, V>
举例:
Last updated