【练习13-1(IGenQ.java、QueueFullException.java、QueueEmptyException.java、GenQueue.java、GenQDemo.java)】创建一个泛型队列
泛型带给程序设计的最大好处之一就是能够创建可靠、可重用的代码。如本章开头所述,许多算法不管使用的数据类型是什么、算法逻辑都是相同的。例如,队列的工作方式对于整数、字符串或File对象都是相同的。不必为每一种类型的对象分别创建独立的队列类,创建单个通用的解决方案就可以使用任何类型的对象。这样,设计、编码、测试和调试的开发过程可以一次完成,而不必在每次队列需要一种新数据类型时重复进行。
本练习将把从练习5-2开始开发的队列示例改进为泛型队列,以便最终完成这个队列程序。该程序包括一个定义了队列操作的泛型接口、两个异常类和一种队列实现方式:固定大小的队列。当然,读者也可以试验其他类型的泛型队列,如泛型动态队列或泛型循环队列,只要在本练习的基础上改进即可。
同前面的练习9-1中开发的队列一样,本练习也将队列组织到一组单独的文件中:一个代表接口,一个代表队列异常类,一个代表固定队列的实现方式,一个代表演示队列的程序。这种组织方式仿照了实际环境中的项目组织方式。
package javaone.a.beginners.guide.chapterthirteen;
// A generic queue interface.
interface IGenQ<T>{
// Put an item into the queue.
void put(T ch) throws QueueFullException;
// Get an item from the queue.
T get() throws QueueEmptyException;;
}
// An exception for queue-full errors.
class QueueFullException extends Exception{
int size;
QueueFullException(int s){
size = s;
}
public String toString(){
return "\nQueue is full. Maximum size is " + size;
}
}
// An exception for queue-empty errors.
class QueueEmptyException extends Exception{
public String toString(){
return "\nQueue is empty.";
}
}
// A generic, fixed-size queue class.
class GenQueue<T> implements IGenQ<T>{
private T q[]; // this array holds the queue
private int putloc, getloc; // the put and get indices
// Construct an empty queue with the given array.
public GenQueue(T[] aRef){
q = aRef;
putloc = getloc = 0;
}
// Put an item into the queue.
public void put(T ch) throws QueueFullException{
if(putloc == q.length){
throw new QueueFullException(q.length);
}
q[putloc++] = ch;
}
// Get a character from the queue.
public T get() throws QueueEmptyException{
if(getloc == putloc){
throw new QueueEmptyException();
}
return q[getloc++];
}
}
/*
Try This 13-1
Demonstrate a generic queue class.
*/
public class ChapterThirteenProgramOne {
public static void main(String[] args) {
// Create an integer queue.
Integer iStore[] = new Integer[10];
GenQueue<Integer> q = new GenQueue<Integer>(iStore);
Integer iVal;
System.out.println("Demonstrate a queue of Integers.");
try{
for (int i = 0; i < 5; i++) {
System.out.println("Adding " + i + " to q.");
q.put(i); // add integer value to q
}
}catch (QueueFullException exc){
System.out.println(exc);
}
System.out.println();
try{
for (int i = 0; i < 5; i++) {
System.out.print("Getting next Integer from q: ");
iVal = q.get();
System.out.println(iVal);
}
}catch (QueueEmptyException exc){
System.out.println(exc);
}
System.out.println();
// Create a Double queue.
Double dStore[] = new Double[10];
GenQueue<Double> qTwo = new GenQueue<Double>(dStore);
Double dVal;
System.out.println("Demonstrate a queue of Doubles.");
try{
for (int i = 0; i < 5; i++) {
System.out.println("Adding " + (double)i/2 + " to qTwo.");
qTwo.put((double)i/2);
}
}catch (QueueFullException exc){
System.out.println(exc);
}
System.out.println();
try{
for (int i = 0; i < 5; i++) {
System.out.print("Getting next Double from qTwo: ");
dVal = qTwo.get();
System.out.println(dVal);
}
}catch (QueueEmptyException exc){
System.out.println(exc);
}
}
}
13.15 自测题
1. 泛型是Java添加的一项重要功能,因为它使得代码的创建:
A. 类型安全
B. 可重用
C. 可靠
D. 以上都对
答案:D
2. 基本类型能用作类型实参吗?
答案: 不能,类型实参必须是对象类型。
3. 说明如何声明一个名为FlightSched的类,它带有两个泛型形参。
答案:class FlightSched<T, V> {
4. 对于自测题3,修改FlightSched的第二个类型形参,使得它必须扩展Thread。
答案:class FlightSched<T, V extends Thread>
5. 现在,修改FlightSched的第二个类型形参,使它成为第一个类型形参的子类。
答案:class FlightSched<T, V extends T>
6. 就泛型而言,"?"代表什么?它的作用是什么?
答案:“?"是通配符实参,能匹配任何有效的类型。
7. 可以约束通配符实参吗?
答案:可以,通配符实参既可以有上层约束,也可以有下层约束。
8. 泛型方法MyGen()带有一个类型形参。而且,MyGen()带有一个其类型与该类型形参相同的形参。它还返回该类型形参的对象。写出如何声明MyGen()。
答案: <T> T MyGen(T o) {
9. 对于下面的泛型接口:
interface IGenIF<T, V extends T> { // ...
提供实现IGenIF的MyClass类的声明。
答案: class MyClass<T, V extends T> implements IGenIF<T, V> { // ...
10. 对于泛型类Counter<T>,说明如何创建一个其原类型的对象。
答案:要获得Counter<T>的原类型,在不带任何类型规范的情况下只需要使用其名称即可,如下所示:
Counter x = new Counter();
11. 类型形参在运行时存在吗?
答案:不存在,所有的类型形参在编译时都被擦除,由合适的强制转换替代。这一过程称为擦除。
12. 把第9章自测题10中的解决方案 转换为泛型解决方案。在转换过程中,创建一个以泛型方式定义操作push()和pop()的堆栈接口IGenStack。
package javaone.a.beginners.guide.chapterthirteen;
// A generic stack.
interface IGenStack<T> {
void push(T t) throws StackFullException;
T pop() throws StackEmptyException;
}
// An exception for stack-full errors.
class StackFullException extends Exception {
int size;
StackFullException(int s) {
size = s;
}
public String toString() {
return "\nStack is full. Maximum size is " +size;
}
}
// An exception for stack-empty errors.
class StackEmptyException extends Exception {
public String toString() {
return "\nStack is empty.";
}
}
// A stack class for characters.
class GenStack<T> implements IGenStack<T> {
private T stck[]; // this array holds the stack
private int tos; // top of stack
// Construct an empty stack given its size.
GenStack(T[] stckArray){
stck = stckArray;
tos = 0;
}
// Construct a stack from a stack
GenStack(T[] stackArray, GenStack<T> ob){
tos = ob.tos;
stck = stackArray;
try {
if(stck.length < ob.stck.length){
throw new StackFullException(stck.length);
}
}catch (StackFullException exc){
System.out.println(exc);
}
// copy elements.
for (int i = 0; i < tos; i++) {
stck[i] = ob.stck[i];
}
}
// Construct a stack with initial values.
GenStack(T[] stackArray, T[] a){
stck = stackArray;
for (int i = 0; i < a.length; i++) {
try {
push(a[i]);
}catch (StackFullException exc){
System.out.println(exc);
}
}
}
// Pusg objects onto the stack.
public void push(T obj) throws StackFullException {
if(tos == stck.length){
throw new StackFullException(stck.length);
}
stck[tos] = obj;
tos++;
}
// Pop an object from the stack.
public T pop() throws StackEmptyException {
if(tos == 0){
throw new StackEmptyException();
}
tos--;
return stck[tos];
}
}
// Demonstrate the GenStack class.
public class ChapterThirteenExerciseTwelve {
public static void main(String[] args) {
// Construct 10-element empty Integer stack.
Integer iStore[] = new Integer[10];
GenStack<Integer> stkOne = new GenStack<Integer>(iStore);
// Construct stack from array.
String name[] = {"One","Two","Three"};
String strStore[] = new String[3];
GenStack<String> stkTwo = new GenStack<String>(strStore,name);
String str;
int n;
try{
// Put some values into stkOne
for (int i = 0; i < 10; i++) {
stkOne.push(i);
}
}catch (StackFullException exc){
System.out.println(exc);
}
// Construct stack from another stack.
String strStoreTwo[] = new String[3];
GenStack<String> stkThree = new GenStack<String>(strStoreTwo,stkTwo);
try{
// Show the stacks.
System.out.print("Contents of stkOne: ");
for (int i = 0; i < 10; i++) {
n = stkOne.pop();
System.out.print(n + " ");
}
System.out.println("\n");
System.out.print("Contents of stkTwo: ");
for (int i = 0; i < 3; i++) {
str = stkTwo.pop();
System.out.print(str + " ");
}
System.out.println("\n");
System.out.print("Contents of stkThree: ");
for (int i = 0; i < 3; i++) {
str = stkThree.pop();
System.out.print(str + " ");
}
}catch (StackEmptyException exc){
System.out.println(exc);
}
System.out.println();
}
}
13. <>是什么?
答案:是菱形运算符用于类型推断。
14. 如何简化下面的语句?
MyClass<Double, String> obj = new MyClass<Double, String>(1.1, "Hi");
答案:
MyClass<Double, String> obj = new MyClass<>(1.1, "Hi");