如上面“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
将下列代码添加到“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 '
首先看一下Flea
。Set anim = fl
Call anim.Bite(ty) 'Flea
叮咬dinosaur
。'
现在轮到Tyrannosaur
。Set anim = ty
Call anim.Bite(fl) 'Dinosaur
咬flea
。End Sub
按 F8 键,单步执行代码。注意“立即”窗口中的信息。当 变量 anim
包含对 Flea 的引用时,就调用该 Flea 的 Bite 实现,同样的,对于 Tyrannosaur 来说,情况也是如此。
变量 anim
可以包含对实现 Animal 接口的任何对象的引用。事实上,它只能包含对这种类型对象的引用。如果试图将一个 Form 或者一个 PictureBox 对象赋给 anim
的话,那么将会产生错误。
当通过 anim
来调用 Bite 方法时,该方法是前期绑定的,因为 Visual Basic 在编译时知道,不论将什么对象赋给 anim
,该对象都将有一个 Bite 方法。
回忆一下“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
为了返回值的赋值,使用全过程名,包括接口前缀。
详细信息 所实现的接口,除方法外还可以有属性。下面“实现属性”讨论了属性实现的方式上的一些不同之处。