第六章 接 口
简单地说,接口就是一种大家都必须遵守的协议。特别是团队开发或利用已有的组件,接口的应用是必不可少的。接口是一种类似于类的引用类型,它的成员包括常量(Constants)和抽象方法(Abstract Methods)。
接口可以声明为对一个或多个其它接口的“直接继承”,这意味着除了被隐藏的常量,接口将隐式地包含它所继承的全部接口中的所有常量和抽象方法。
类可以直接实现(directly implements)一个或多个接口,这种类的任何一个实例都要求实现接口中的所有抽象方法。类必须实现其基类和基接口中指定的所有接口。这种对接口的继承允许对象在不共享任何实现的前提下支持相同的行为。
§6.1 接口声明
接口定义协议,类实现此接口定义。接口可以包含方法、常量,但接口本身并不提供这些成员的实现,它仅指定大家需要遵守的协议,或者说需要使用的成员,由实现该接口的类来具体地实现这些成员。
<接口声明>定义为:
[<修饰符>] interface <标识符>[ :<基接口表>] <定义体>
<基接口表>定义为:
<接口名>{
,<接口名>}
<修饰符>是以下保留字之一:
public internal protected private
其中<修饰符>和<基接口>是可选项。<标识符>为接口名。
<基接口表>的作用是指定该声明接口的直接基接口。一个接口可以继承多个接口,如果继承了多个接口,那么在接口声明时,多个基接口之间采用逗号(,)分隔。
接口的继承具有传递性,看了下面的例子就能理解这种传递性:
interface IMyInterface1
{
F1() = bool
}
interface IMyInterface2: IMyInterface1
{
F2( int v ) = bool
}
interface IMyInterface3: IMyInterface1
{
F3( String str ) = bool
}
interface IMyInterface4: IMyInterface2, IMyInterface3
{
}
在上面这段代码中,接口IMyInterface4的基接口为IMyInterface1、IMyInterface2和IMyInterface3,其中IMyInterface1是通过IMyInterface2传递过来的。此外,IMyInterface4将继承其基类中的所有成员。这样,接口IMyInterface4将继承函数F1、F2和F3。
§6.2 接口的成员
一个接口的成员不止包括自身声明的成员,如果该接口有基接口,那么它还包括从基接口那里继承来的成员。一个接口可以声明一个或多个成员。接口成员必须是方法和常量。接口不能包括字段、运算符、构造函数、类型,也不能包含任何种类的静态成员。
接口成员的访问权限缺省为public。在声明接口成员时,不能显式地指定任何修饰符,否则会出现编译错误。
§6.2.1 常量成员
接口中的常量声明文法和类中的常量声明相同,只是接口中常量声明不得包含任何修饰符和风格定义。Fuxi中接口常量固定为静态的。例如:
interface IShape
{
double PI = 3.14159
getArea() = double
}
§6.2.2 方法成员
接口中的方法成员的声明文法和类的方法原型相同,只是接口中的方法原型不得包含任何修饰符和风格定义。接口中的方法固定为公开的抽象方法。
§6.3 接口的实现
接口可以由类来实现。为了表明一个类实现了某个接口,在该类声明时将在其基类列表中包含接口标识符。我们来看一个例子:
import fuxi.*
interface Shape
{
double PI = 3.14159
getArea() = double
getPerimeter() = double
}
class Square: Shape
{
public int x, y
public int w, h
public getArea() = (double)(w * h)
public getPerimeter() = (double)(w + h) * 2
public Square( int x, int y, int w, int h ) =
{
this.x := x
this.y := y
this.w := w
this.h := h
}
}
class Circle: Shape
{
public int x, y
public double r
public getArea() = PI * r * r
public getPerimeter() = 2 * PI * r
public Circle( int x, int y, double r ) =
{
this.x := x
this.y := y
this.r := r
}
}
public active class ShapesApp: Application
{
Square Box( 5, 15, 25, 25 )
Circle Oval( 5, 50, 25.0 )
}
可以看出,Square和Circle类定义了Shape接口中的所有的抽象方法,从而实现了Shape接口。这个程序的执行结果为:
实现某个接口的类也必须同时实现该接口的所有基接口。即使在声明时,类并没有显式地列出所有基接口,该类也必须实现基接口中的所有方法。看下面的程序段:
interface IControl
{
Draw() = bool
}
interface IEditBox: IControl
{
SetText( String str ) = bool
}
class CEditBox: IEditBox
{
public Draw() = { ... }
public SetText( String str ) = { ... }
}
CEditBox实现了接口IEditBox的成员函数SetText,同时也实现了接口IEditBox的基接口IControl的成员函数Draw。
§6.3.1 接口映射
如果某接口出现在一个类的基类列表中,那么该类就必须提供此接口的所有成员的实现。在一个类中定位接口成员的实现的过程称为接口映射(Interface mapping)。
一个类C的接口映射定位C的基类列表中出现的所有接口的每一个成员的实现。对一个特定的接口I.M(I是接口名,M是该接口的一个成员)的映射过程:从类C开始,接着对C的基类S进行检查,直到找到与之匹配的成员为止。如果C中包含一个与I.M匹配的成员,那么这个成员就是I.M的直接实现,否则如果S中包含一个与I.M匹配的非静态公开成员声明,那么这个成员就是I.M的实现。
如果无法定位类C的基类列表中列出的所有接口的每个成员,那么会出现编译错误。
注意:一个接口成员包括那些从基接口中继承的成员。
为了接口映射的目的,一个类成员A与接口成员B匹配的条件如下:
A和B必须都是方法成员,并且它们必须具有相同的签名。
Private、和static类型的成员不参加接口映射。
如果一个类实现两个或两个以上的接口,并且这些接口的某些成员具有相同的名称、类型和参数表,这是可以将这些接口成员映射成类中的一个成员。请看例子:
interface IControl
{
Draw() = bool
}
interface IForm
{
Draw() = bool
}
class Page: IControl, IForm
{
public Draw() = { ... }
}
这里,接口IControl和IForm都具有成员Draw,它们都映射到类Page中的一个成员,既Draw函数。
对接口的实现还有一种特殊的情况:如果类实现多个接口,而这些接口中有些接口具有相同的基接口,那么此时该类只需要实现一次该基接口。如以下代码所示:
interface IControl
{
Draw() = bool
}
interface IEditBox: IControl
{
SetText(String str) = bool
}
interface IListBox: IControl
{
SetItem( int i, String item ) = bool
}
class CComboBox: IControl, IEditBox, IListBox
{
Draw() = { ... }
SetText( String str ) = { ... }
SetItem( int I, String str ) = { ... }
}
接口IEditBox和IListBox都继承了接口IControl,而它们的实现类CComboBox只需要实现一次接口IControl的成员Draw。
§6.3.2 接口实现的继承
类继承它的基类提供的所有接口的实现。如果没有重实现一个接口,派生类无论如何也不能改变它从基类那里继承来的接口映射关系。看下面的例子:
interface IControl
{
Draw() = bool
}
class Control: IControl
{
public Draw() = { ... }
}
class EditBox: Control
{
public Draw() = { ... }
}
类EditBox的Draw函数隐藏了基类Control中的Draw函数,但它并没有改变Control.Draw对IControl.Draw的映射关系。
§6.3.3 接口的重实现
一个继承了接口实现的类仍可以重实现(Re-implement)该接口,方法是在该类的基类列表中再次包含这个接口。
接口的重实现同样遵循接口初始实现的映射规则。这样,继承的类的接口映射对接口的重实现的映射关系的建立没有任何影响。例如:
interface IControl
{
Draw() = bool
}
class Control: IControl
{
public Draw() = { ... }
}
class MyControl: Control, IControl
{
public Draw() = { ... }
}
在上述代码中,类Control将IControl.Draw函数映射到Control.Draw函数,而类MyControl将IControl.Draw函数映射到MyControl.Draw函数上,前者对后者没有什么影响。
|