泛型类型(**generic type**)是通过类型进行参数化而形成的泛型类或接口。下面的Box类将被修改以说明该概念。
一个简单的Box类
首先检查一个非泛型Box类,它对任何类型的对象进行操作。它只需要提供两种方法:将对象添加到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中,并期望从中取出Integer,而代码的另一部分可能会错误地传入String,从而导致运行时错误。
Box类的泛型版本
一个泛型类(generic class)的定义格式如下:
class name<T1, T2, ..., Tn> { /* ... */ }
类名后面是用尖括号(<>)分隔的类型形参部分。它指定类型形参(**type parameters**)(也称为类型变量(**type variables**))T1,T2,…和Tn。
要更新Box类以使用泛型,可以通过将代码“ public class Box ”更改为“ public class Box
进行此更改后,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代替。类型变量可以是您指定的任何非基本类型:任何类类型(class type),任何接口类型(interface type),任何数组类型(array type),甚至另一个类型变量(type variable)。
可以将相同的技术应用于创建泛型接口。
类型形参命名约定
按照约定,类型形参名称是单个大写字母。这与您已经知道的变量命名约定形成鲜明对比 ,并且有充分的理由:没有该约定,将很难分辨类型变量与普通类或接口名称之间的区别。
最常用的类型形参名称是:
- E——元素(由Java Collections Framework广泛使用)
- K——键
- N——数字
- T——类型
- V——值
- S,U,V等——第二,第三,第四类型
您将在Java SE API以及本课程其余部分看到使用的这些名称。
调用和实例化泛型类型
要从代码中引用泛型Box类,您必须执行泛型类型调用(generic type invocation),该调用将T替换为某些具体值,例如Integer:
Box<Integer> integerBox;
您可以认为泛型类型调用类似于普通方法调用,但是您没有将实参传递给方法,而是将类型实参(在这种情况下为Integer)传递给Box类本身。
Type Parameter(类型形式参数,类型形参)和Type Argument(类型实际参数,类型实参)术语: 许多开发人员经常互换使用术语“type parameter”和“type argument”,但是这些术语并不相同。编码时,提供类型实参(type argument)以创建参数化类型(parameterized type)。因此,在Foo
(个人注解:Type Parameter(类型形参)和Type Argument(类型实参)的关系,类似于形参与实参的关系,只是前者针对类型而言)
像任何其他变量声明一样,此代码实际上不会创建新的Box对象。它只是声明integerBox将保存对“Box of Integer” 的引用,这就是读取Box
泛型类型的调用通常称为参数化类型(**parameterized type**)。
要实例化此类,请像往常一样使用new关键字,但将
Box<Integer> integerBox = new Box<Integer>();
菱形
在Java SE 7和更高版本中,只要编译器可以从上下文确定或推断出类型实参(type arguments),就可以用一组空的类型实参(<>),替换调用泛型类的构造器所需的类型实参。 这对尖括号<>非正式地称为菱形(diamond)。例如,您可以使用以下语句创建Box
Box<Integer> integerBox = new Box<>();
有关菱形符号和类型推断的更多信息,请参见 类型推断。
多种类型形参
如前所述,泛型类可以具有多个类型形参。例如,泛型类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; }
}
以下语句创建OrderedPair类的两个实例:
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");
代码new OrderedPair
如菱形所述,由于Java编译器可以从声明OrderedPair
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
参数化类型
您也可以用参数化类型(即List
OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));