Java 泛型入门
假如现在要实现一个栈(Stack)(一种用于存储数据的类),那么这个栈应该支持存储任意类型的数据,无论是 Integer、String 还是我们自己定义的数据类型。Java 泛型机制能够做到这一点。泛型也叫做参数化类型,支持泛型的类简称泛型类。
参数化类型,就是让类支持接受数据类型(类)作为参数,在创建泛型类对象的时候,我们将具体的类型传递给泛型类,进而创建出支持具体类型的类对象。
比如,假如 Stack 是泛型类,那么,我们可以将 String 类型作为参数传递给 Stack 类,以创建支持处理 String 类型的 Stack 对象。
Stack<String> stack = new Stack<String>();
stack.push("Test");
...
String next = stack.pop();
如果没有泛型,我们必须为所需要支持的每种数据类型定义(并实现)不同的 API。有了泛型,我们只需要一份 API(和一次实现)就能够处理所有类型的数据。所以泛型简化了代码。
同时,如果你尝试向支持 String 类型的 Stack 对象中添加一个 Integer 对象(或是任何其他非 String 类型的数据),你会得到一个编译时错误。所以,泛型使代码更加“静态”,减少了出错的机会。
那么如何实现一个泛型类,也就是如何运用泛型让普通类支持处理所有数据类型?
比如这是一个未实现泛型的定容栈(FixedCapacityStack)(一种初级栈),它目前只支持存储 String 类型的对象:
public class FixedCapacityStack
{
private String[] a;
private int N;
public FixedCapacityStack(int cap) { a = new String[cap]; }
public boolean isEmpty() { return N == 0; }
public int size() { return N; }
public void push(String item) { a[N++] = item; }
public String pop() { return a[--N]; }
public boolean isFull() { return N == a.length; }
}
要将 FixedCapacityStack 泛型化,首先要像定义带参数函数那样在类定义中添加类型参数:
public class FixedCapacityStack<Item>
{
...
}
相比于非泛型的类定义,上述定义多了一个 <Item>
,这就是 FixedCapacityStack 类的类型参数。
加了类型参数之后,再将类中所有用到具体类型(String)的地方用 Item 代替(类似于在函数内部定义如何使用传递过来的参数):
public class FixedCapacityStack<Item>
{
private Item[] a;
private int N;
public FixedCapacityStack(int cap) { a = (Item[]) new Object[cap]; }
public boolean isEmpty() { return N == 0; }
public int size() { return N; }
public void push(Item item) { a[N++] = item; }
public Item pop() { return a[--N]; }
public boolean isFull() { return N == a.length; }
}
这样就实现了 FixedCapacityStack 的泛型类。
有一个注意点是,在非泛型的 FixedCapacityStack 类中,使用 a = new String[cap];
语句创建字符串数组,而在 FixedCapacityStack<Item>
中,我们用 a = (Item[]) new Object[cap];
语句创建泛型数组,能不能用 a = new Item[cap];
语句来创建?不能。直接创建泛型数组在 Java 中是不允许的。
总结一下,实现一个泛型类:
第一步:在类定义中添加泛型标识 <Item>
;
第二步:将类中(除创建泛型数组外的)所有用到具体类型的地方用 Item 代替;
第三步:需要创建泛型数组时,使用类型转换(a = (Item[]) new Object[cap];
)来间接创建泛型数组。
总结自《算法(第四版)》1.3 背包、队列和栈