创建和实现接口

如上面“Visual Basic 是如何提供多态的”中所解释的,所谓接口,就是一组属性和方法。在下面的代码示例中,将创建一个 Animal 接口,并在两个类— Flea 和 Tyrannosaur 中实现它。

创建该 Animal 接口的方法是:将一个类模块添加到工程中,将它命名为 Animal,并插入到下面的代码中:

Public Sub Move(ByVal Distance As Double)

End Sub

Public Sub Bite(ByVal What As Object)

End Sub

注意,在这些方法中并没有任何代码。Animal 是一个抽象类,不包含实现的任何代码。抽象类不是用来创建对象的—其用途是为添加其它类中的接口提供模板。(尽管,已经证明,有些时候实现一个非抽象类的接口是有用的;这将在本节主题的后面进行讨论。)

注意   确切地说,一个抽象的类是不能从中创建对象的类。从 Visual Basic 的类中总是可以创建对象的,即使它们不含有代码,它们也不是真正抽象的。

现在,就可以添加另外两个类模块了,这两个类模块一个叫 Flea,另一个叫 Tyrannosaur。为了在 Flea 类中实现 Animal 接口,要用到 Implements 语句:

Option Explicit
Implements Animal

一旦将这行代码添加进去,就可以单击代码窗口中左边(“对象”)下拉菜单。其中一个登录项将是 Animal。当选择了它时,右边(“过程”)下拉菜单上将显示 Animal 接口的方法。

依次选择每种方法,可为所有的方法创建空白过程模板。这些模板将具有正确的参数和数据类型,就象在 Animal 类中所定义的那样。每个过程名都将以 Animal_ 前缀来标识该接口。

重点   一个接口就象一个契约。实现接口后,当调用该接口的任何属性或者方法时,一个类已经约定好要作出反应。因此,必须实现接口的所有属性和方法。

现在,就可以将下面的代码添加到 Flea 类中了:

Private Sub Animal_Move(ByVal Distance As Double)
   '(跳过多少英寸的代码,略。)
   Debug.Print "Flea moved"
End Sub

Private Sub Animal_Bite(ByVal What As Object)
   '(吃了多少生命的代码,略。)
   Debug.Print "Flea bit a " & TypeName(What)
End Sub

可能想知道为什么这些过程被声明为 Private。如果它们是 Public,那么 Animal_Jump 和 Animal_Bite 过程将成为 Flea 界面的一部分,这样将被困在跟原先所在的同样的圈子中,将 Critter 参数声明为 As Object,这样它就可能包含一个 Flea 或者一个 Tyrannosaur。

多重接口

现在,Flea 类有了两个接口:刚刚实现的 Animal 接口—它有两个成员,以及缺省的 Flea 接口—它没有任何成员。该示例的后面,将把一个成员添加到缺省接口的其中一个。

类似地,可以为 Tyrannosaur 类实现 Animal 接口:

Option Explicit
Implements Animal

Private Sub Animal_Move(ByVal Distance As Double)
   '(跳起多少码的代码,略。)
   Debug.Print "Tyrannosaur moved"
End Sub

Private Sub Animal_Bite(ByVal What As Object)
   ' (拿起一磅肉的代码,略。)
   Debug.Print "Tyrannosaur bit a " & TypeName(What)
End Sub

练习 Tyrannosaur 和 Flea

将下列代码添加到“Form1”的 Load 事件中:

Private Sub Form_Load()
   Dim fl As Flea
   Dim ty As Tyrannosaur
   Dim anim As Animal

   Set fl = New Flea
   Set ty = New Tyrannosaur
   '首先看一下 FleaSet anim = fl
   Call anim.Bite(ty)   'Flea 叮咬 dinosaur'现在轮到 TyrannosaurSet anim = ty
   Call anim.Bite(fl)   'Dinosaur  fleaEnd Sub

F8 键,单步执行代码。注意“立即”窗口中的信息。当 变量 anim 包含对 Flea 的引用时,就调用该 Flea 的 Bite 实现,同样的,对于 Tyrannosaur 来说,情况也是如此。

变量 anim 可以包含对实现 Animal 接口的任何对象的引用。事实上,它只能包含对这种类型对象的引用。如果试图将一个 Form 或者一个 PictureBox 对象赋给 anim 的话,那么将会产生错误。

当通过 anim 来调用 Bite 方法时,该方法是前期绑定的,因为 Visual Basic 在编译时知道,不论将什么对象赋给 anim,该对象都将有一个 Bite 方法。

将 Tyrannosaurs 和 Fleas 传递给过程

回忆一下“Visual Basic 如何提供多态?”("How Visual Basic Provides polymorphism?")中的 GetFood 过程。可以将 GetFood 过程的第 2 个版本—演示多态的版本—添加到“Form1”中,并用下面的代码来取代 Load 事件中的代码:

Private Sub Form_Load()
   Dim fl As Flea
   Dim ty As Tyrannosaur

   Set fl = New Flea
   Set ty = New Tyrannosaur
   'Flea 在恐龙上吃饭。
   Call GetFood(fl, ty)
   '相反的情况。
   Call GetFood(ty, fl)
End Sub

单步执行上述代码,显示作为传递给另一接口类型参数的对象引用,是怎样转换为对第二个接口(在这里是 Animal)的引用的。所发生的情况是: Visual Basic 查询该对象,看看该对象是否支持第二个接口。如果该对象支持的话,它将返回对该接口的引用,于是 Visual Basic 将返回的引用放置到参数变量中。如果该对象不支持第二个接口,将会出现错误。

实现返回值的方法

假设 Move 方法返回一个值。不管怎么说,自己知道想让 Animal 移动多远的距离,但是,各个实例不可能都移动那么远。它可能又老又弱,或者在路上可能恰好有一堵墙。可以用 Move 方法的返回值来说明该 Animal 实际移动了多远的距离。

Public Function Move(ByVal Distance As Double) _
As Double

End Function

在 Tyrannosaur 类中实现该方法时,把返回值赋给过程名,这跟处理任何其它 Function 过程一样:

Private Function Animal_Move(ByVal Distance _
As Double) As Double
   Dim dblDistanceMoved As Double
   '计算能弹跳多远(基于对年龄、健康状态和障碍物的
   '考虑)的代码,略。
   '该示例假设已经将结果放置到变量 dblDistanceMoved 中。
   Debug.Print "Tyrannosaur moved"; dblDistanceMoved
   Animal_Move = dblDistanceMoved
End Function

为了返回值的赋值,使用全过程名,包括接口前缀。

详细信息   所实现的接口,除方法外还可以有属性。下面“实现属性”讨论了属性实现的方式上的一些不同之处。