Java基础篇:如何应用接口?

115 阅读3分钟

为理解接口的功能,让我们看一个更实际的例子。我们曾开发过一个名为Stack的类,该类实现了一个简单的固定大小的堆栈。然而,有很多方法可以实现堆栈。例如,堆栈的大小可以固定也可以不固定。堆栈还可以保存在数组、链表和二进制树中等。

无论堆栈怎样实现,堆栈的接口保持不变。也就是说,push( )和pop( )方法定义了独立实现细节的堆栈的接口。因为堆栈的接口与它的实现是分离的,很容易定义堆栈接口,而不用管每个定义实现细节。让我们看下面的两个例子。

下面定义了一个整数堆栈接口,把它保存在一个IntStack.java文件中。该接口将被两个堆栈实现使用。

// Define an integer stack interface. 
interface IntStack { 
 void push(int item); // store an item 
 int pop(); // retrieve an item 
} 

下面的程序创建了一个名为FixedStack的类,该类实现一个固定长度的整数堆栈:

// An implementation of IntStack that uses fixed storage. 
class FixedStack implements IntStack { 
 private int stck[]; 
 private int tos; 
 // allocate and initialize stack 
 FixedStack(int size) { 
 stck = new int[size]; 
 tos = -1; 
 } 
 // Push an item onto the stack 
 public void push(int item) { 
 if(tos==stck.length-1) // use length member 
 System.out.println("Stack is full."); 
 else 
 stck[++tos] = item; 
 } 
 // Pop an item from the stack 
 public int pop() { 
 if(tos < 0) { 
 System.out.println("Stack underflow."); 
 return 0; 
 } 
 else 
 return stck[tos--]; 
 } 
} 
class IFTest { 
 public static void main(String args[]) { 
 FixedStack mystack1 = new FixedStack(5); 
 FixedStack mystack2 = new FixedStack(8); 
 // push some numbers onto the stack 
 for(int i=0; i<5; i++) mystack1.push(i); 
 for(int i=0; i<8; i++) mystack2.push(i); 
 // pop those numbers off the stack 
 System.out.println("Stack in mystack1:"); 
 for(int i=0; i<5; i++) 
 System.out.println(mystack1.pop()); 
 System.out.println("Stack in mystack2:"); 
 for(int i=0; i<8; i++) 
 System.out.println(mystack2.pop()); 
 } 
} 

下面是IntStack 的另一个实现。通过运用相同的接口定义IntStack 创建了一个动态堆栈。这种实现中,每一个栈都以一个初始长度建造。如果初始化长度被超出,那么堆栈的大小将增加。每一次需要更多的空间,堆栈的大小成倍增长。

// Implement a "growable" stack. 
class DynStack implements IntStack { 
 private int stck[]; 
 private int tos; 
 // allocate and initialize stack 
 DynStack(int size) { 
 stck = new int[size]; 
 tos = -1; 
 } 
 // Push an item onto the stack 
 public void push(int item) { 
 // if stack is full, allocate a larger stack 
 if(tos==stck.length-1) { 
 int temp[] = new int[stck.length * 2]; // double size 
 for(int i=0; i<stck.length; i++) temp[i] = stck[i]; 
 stck = temp; 
 stck[++tos] = item; 
 } 
 else 
 stck[++tos] = item; 
 } 
 // Pop an item from the stack 
 public int pop() { 
 if(tos < 0) { 
 System.out.println("Stack underflow."); 
 return 0; 
 } 
 else 
 return stck[tos--]; 
 } 
} 
class IFTest2 { 
 public static void main(String args[]) { 
 DynStack mystack1 = new DynStack(5); 
 DynStack mystack2 = new DynStack(8); 
 // these loops cause each stack to grow 
 for(int i=0; i<12; i++) mystack1.push(i); 
 for(int i=0; i<20; i++) mystack2.push(i); 
 System.out.println("Stack in mystack1:"); 
 for(int i=0; i<12; i++) 
 System.out.println(mystack1.pop()); 
 System.out.println("Stack in mystack2:"); 
 for(int i=0; i<20; i++) 
 System.out.println(mystack2.pop()); 
 } 
} 

下面的类运用了FixedStack 和DynStack 实现。它通过一个接口引用完成。意思是说对push( ) 和 pop( )的调用在运行时解决而不是在编译时解决。

/* Create an interface variable and 
 access stacks through it. 
*/ 
class IFTest3 { 
 public static void main(String args[]) { 
 IntStack mystack; // create an interface reference variable 
 DynStack ds = new DynStack(5); 
 FixedStack fs = new FixedStack(8); 
 mystack = ds; // load dynamic stack 
 // push some numbers onto the stack 
 for(int i=0; i<12; i++) mystack.push(i); 
 mystack = fs; // load fixed stack 
 for(int i=0; i<8; i++) mystack.push(i); 
 mystack = ds; 
 System.out.println("Values in dynamic stack:"); 
 for(int i=0; i<12; i++) 
 System.out.println(mystack.pop()); 
 mystack = fs; 
 System.out.println("Values in fixed stack:"); 
 for(int i=0; i<8; i++) 
 System.out.println(mystack.pop()); 
 } 
} 

该程序中,mystack是IntStack接口的一个引用。因此,当它引用ds时,它使用DynStack实现所定义的push( )和pop( )方法。当它引用fs时,它使用FixedStack定义的push( )和pop( )方法。

已经解释过,这些决定是在运行时做出的。通过接口引用变量获得接口的多重实现是Java完成运行时多态的最有力的方法。