泛型类型是对类型进行参数化的泛型类或接口。接下来我们会修改box类来演示这一概念。
一个简单的Box类
首先检查对任何类型的对象进行操作的非泛型类。它只需要提供2个方法:向box添加对象的set方法,以及get方法。
public class Box {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
既然,它的方法接收或者返回一个object,你可以使用任何类型,只要它不是原始类型之一。这没有办法在编译时去校验。一部分代码可以放置一个Integer到box中,期望能够从中获取integers。然而另一方面,可能错误的防止了String类型,在运行时就会出错。
Box类的泛型版本
泛型类的定义格式:
class name<T1, T2, ..., Tn> { /* ... */ }
在类型参数部分,被<>分割,紧随着类名。它指定了类型参数 T1,T2……Tn.
更新的box类中使用了泛型,你通过修改代码“public class Box”为“public class Box< T>”来声明一个泛型类型。这个类型变量T可以在这个类中的任何地方使用。
修改后的Box类变成了这样:
/**
* Generic version of the Box class.
* @param <T> 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; }
}
如你所见,所有出现的object都被替换为了T,一个类型变量可以为你指定的任何非原始类型:任何类,接口类型,数组类型,甚至是类型变量。
相同的技术可以适用于创建泛型接口。
类型参数命名规范
按照惯例,类型参数是单个大写的字母。这和你已经知道的变量命名规范成为一个鲜明的对比,而且这样写的原因为:没有这个规范,将要很难区分类型变量和普通类,接口名字的不同。
最常见的类型参数名称为:
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
调用和实例化一个泛型类型
在你的代码中参考这个泛型类,你必须执行泛型类型调用,替换T为具体的值,例如Integer:
Box<Integer> integerBox;
你可以将泛型类型调用看做和普通方法调用类似,但是不是传递参数到一个方法,而是传递一个类型参数。在这个例子中的Integer。
类型参数,参数术语
许多程序员会将type parameter和type argument互换使用。但是他们是不一样的。当编码时,一个提供类型参数以创建一个参数化类型。然而,在Foo< T >中的T是一个类型参数,而Foo< String>中的String是一个具体的参数类型。使用这些术语时,本课程将会遵守此定义。
像其他的变量声明一样,代码不会创建一个新的Box对象。它只是声明integerBox将持有对“Box of Integer”的引用,这是如何读取Box < Integer>。
泛型类型的调用通常称为参数化类型。
为了实例化这个类,我们使用关键字new,像往常一样,但是在类名和括号之间放置< Integer>。
菱形语法
在JavaSE7或者更高版本,只要编译器可以确定,可以使用空的一组类型参数(<>)替换调用泛型类的构造函数所需的类型参数。这对尖角括号<>,被非正式地称为菱形。例如,您可以使用以下语句创建Box < Integer >的实例:
Box<Integer> integerBox = new Box<>();
多种类型参数
在前面提到的,泛型类可以有多种类型参数。例如,实现了泛型接口Pair的泛型类OrderdPair:
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; }
}
下面声明创建了两个OrderedPair类的实例:
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");
代码 new OrderedPair< String, Integer >,将K实例化为String,将V实例化为Integer。因此,OrderedPair的构造函数的参数类型分别为String和Integer。由于自动装箱,将String和int传递给该类是有效的。
在菱形语法一章提到的,因为Java编译器可以通过OfderedPair< String,Integer>来推断k和v的类型,所以这些语句可以使用菱形符号来缩短:
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
创建泛型接口,和泛型类一样,使用同样的规范。
参数化类型
您也可以使用参数化类型(即List )替换类型参数(即K或V)。例如,使用OrderedPair
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));