Java™ 教程(泛型类型)

9次阅读

共计 2642 个字符,预计需要花费 7 分钟才能阅读完成。

泛型类型
泛型类型是通过类型参数化的泛型类或接口,修改以下 Box 类以演示此概念。
一个简单的 Box 类
首先检查一个对任何类型的对象进行操作的非泛型 Box 类,它只需要提供两个方法:set,它将一个对象添加到 box 中,get,它将检索它:
public class Box {
private Object object;

public void set(Object object) {this.object = object;}
public Object get() { return object;}
}
由于它的方法接受或返回一个 Object,所以你可以自由地传入任何你想要的东西,前提是它不是一种原始类型,无法在编译时没有办法验证如何使用该类,代码的一部分可能会在 box 中放置一个 Integer 并期望从中获取 Integer,而代码的另一部分可能会错误地传入 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 — 元素(Java 集合框架广泛使用)
K — 键
N — 数
T — 类型
V — 值
S、U、V 等 — 第二、第三、第四种类型

你将在整个 Java SE API 和本课程的其余部分中看到这些名称。
调用和实例化泛型类型
要从代码中引用泛型 Box 类,必须执行泛型类型调用,它将 T 替换为某些具体值,例如 Integer:
Box<Integer> integerBox;
你可以将泛型类型调用视为与普通方法调用类似,但不是将参数传递给方法,而是将类型参数(在本例中为 Integer)传递给 Box 类本身。

类型参数和类型参数术语:许多开发人员互换地使用术语“类型参数”和“类型实参”,但这些术语并不相同,编码时,提供类型实参以创建参数化类型,因此,Foo<T> 中的 T 是类型参数,而 Foo<String> f 中的 String 是类型实参,本课程在使用这些术语时会遵循此定义。
与任何其他变量声明一样,此代码实际上并不创建新的 Box 对象,它只是声明 integerBox 将保存对“Box of Integer”的引用,这就是 Box<Integer> 的读取方式。
泛型类型的调用通常称为参数化类型。
要实例化此类,请像往常一样使用 new 关键字,但在类名和括号之间放置 <Integer>:
Box<Integer> integerBox = new Box<Integer>();
菱形
在 Java SE 7 及更高版本中,只要编译器可以从上下文中确定或推断类型参数,就可以用一组空的类型参数(<>)替换调用泛型类的构造函数所需的类型参数,这对尖括号 <> 非正式地称为菱形,例如,你可以使用以下语句创建 Box<Integer> 的实例:
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<String, Integer>,将 K 实例化为 String,将 V 实例化为 Integer,因此,OrderedPair 的构造函数的参数类型分别是 String 和 Integer,由于自动装箱,将 String 和 int 传递给类是有效的。
正如菱形中所提到的,因为 Java 编译器可以从声明 OrderedPair<String, Integer> 推断出 K 和 V 类型,所以可以使用菱形表示法缩短这些语句:
OrderedPair<String, Integer> p1 = new OrderedPair<>(“Even”, 8);
OrderedPair<String, String> p2 = new OrderedPair<>(“hello”, “world”);
要创建泛型接口,请遵循与创建泛型类相同的约定。
参数化类型
你还可以使用参数化类型(即 List<String>)替换类型参数(即 K 或 V),例如,使用 OrderedPair<K, V> 示例:
OrderedPair<String, Box<Integer>> p = new OrderedPair<>(“primes”, new Box<Integer>(…));

上一篇:为什么要使用泛型?

正文完
 0