跳转到内容

BlitzMax/语言/用户定义类型

来自维基教科书,开放的书籍,开放的世界

定义类型允许你将相关的数据和程序代码组合成一个名为对象的单一实体。

声明用户定义类型的通用语法是

Type 类型名 Extends 类型名

类型名必须是有效的标识符。Extends 部分是可选的。如果省略,用户定义类型将扩展内置的 Object 类型。

一旦声明,你就可以使用 New 运算符创建此类类型的实例

在用户定义类型中,你可以声明以下内容

  • 字段
字段是与用户定义类型的每个实例相关的变量。字段的声明方式与局部变量或全局变量相同,只是使用 Field 关键字。要访问对象的字段,请使用 . 运算符。
  • 方法
方法是与用户定义类型的每个实例相关的类似函数的操作。方法的声明方式与函数相同,只是使用 Method 关键字。要访问对象的 方法,请使用 . 运算符。方法中的程序代码可以通过名称引用它们来访问同一对象中的其他字段、方法、函数、常量和全局变量。
  • 函数
这些的声明方式与“普通”函数相同,并且可以使用 . 运算符访问。与方法不同,类型中的函数与类型的实例相关联,而是与类型本身相关联。这意味着无论类型是否创建了任何实例,都可以使用此类函数。类型中的函数可以通过名称引用它们来访问同一类型中的其他函数、常量或全局变量。
  • 常量和全局变量
这些的声明方式与“普通”常量和全局变量相同,并且可以使用 . 运算符访问。与类型函数一样,这些不与类型的实例相关联,而是与类型本身相关联。

这是一个用户定义类型的示例

Type MyType
Const INC=1
Global Counter
Field x,y,z

Method Sum()
Counter=Counter+INC
Return x+y+z
End Method

Function Create:MyType()
Return New MyType
End Function
End Type

Local MyObject:MyType=MyType.Create()

MyObject.x=10
MyObject.y=20
MyObject.z=30

Print MyObject.Sum()
Print MyType.Counter

请注意,对象是通过调用 MyType 的 Create 函数而不是 New 来间接创建的。这是一种常用的技术,允许你在将对象返回给用户之前执行(可能是复杂的)初始化。

继承和多态

[编辑 | 编辑源代码]

用户定义类型可以使用 Extends 关键字扩展其他用户定义类型。扩展类型意味着向现有类型添加更多功能。被扩展的类型通常被称为类型,而生成的扩展类型通常被称为派生类型

Type BaseType
Field x,y,z

Method Sum()
Return x+y+z
End Method
End Type

Type DerivedType Extends BaseType
Field p,q,r

Method Sum()
Return x+y+z+p+q+r
End Method
End Type

这种技术也被称为继承,因为派生类型从基类型继承了功能(尽管在此过程中没有人需要死亡!)。请注意,DerivedType 实际上有 6 个字段 - x、y、z、p、q 和 r。它从 BaseType 继承 x、y、z 并添加自己的字段 p、q、r。

BlitzMax 允许你将派生类型对象用于任何需要基类型对象的地方。这是因为派生类型对象基类型对象——只是有一些“额外的东西”。例如,你可以将派生类型对象分配给基类型变量,或者将派生类型对象传递给期望基类型参数的函数。这确实是继承的全部意义所在——它不仅仅是一种节省打字的技术。

这种行为允许一种非常有用的技术,称为多态。这意味着对象能够根据其类型以不同的方式表现。这在 Blitzmax 中通过覆盖方法来实现。

请注意,在上面的示例中,方法 'Sum' 在基类型和派生类型中都具有相同的签名(参数和返回类型)。这不仅仅是巧合——它是语言要求的。每当你向派生类型中添加一个与基类型中现有方法同名的 方法时,它必须与基类型中的方法具有相同的签名。

但现在我们有了 2 个版本的 'Sum' - 哪个会被调用?这取决于对象的运行时类型。例如

Type BaseType
Method Test:String()
Return "BaseType.Test"
End Method
End Type

Type DerivedType Extends BaseType
Method Test:String()
Return "DerivedType.Test"
End Method
End Type

Local x:BaseType=New BaseType
Local y:BaseType=New DerivedType

Print x.Test() 'prints "BaseType.Test" - x's runtime type is BaseType
Print y.Test() 'prints "DerivedType.Test" - as y's runtime type is DerivedType

请注意,当变量 y 初始化时,它被分配了一个 DerivedType 对象,即使 y 是一个 BaseType 变量。这是合法的,因为派生类型可以代替基类型使用。但是,这意味着 y 的运行时类型实际上是 DerivedType。因此,当调用 y.Test() 时,将调用 DerivedType 方法 Test()。

Self 和 Super

[编辑 | 编辑源代码]

方法内部的程序代码可以访问两个名为 SelfSuper 的特殊变量。

Self 指向与方法关联的对象,其类型是声明方法的用户定义类型的类型。

Super 也指向与方法关联的对象,但其类型是正在扩展的用户定义类型的类型。如果你需要调用当前正在执行方法的基类型版本,这将非常有用

Type BaseType
Method Test()
Print "BaseType.Test"
End Method
End Type

Type DerivedType Extends BaseType
Method Test()
Super.Test 'calls BaseType's Test() method first!
Print "DerivedType.Test"
End Method
End Type

Local x:BaseType=New DerivedType

x.Test

New 和 Delete

[编辑 | 编辑源代码]

用户定义类型可以选择声明两个名为 NewDelete 的特殊方法。这两个方法都必须不带参数,并且任何返回值都会被忽略。

当使用 New 运算符首次创建对象时,会调用 New 方法。这允许你执行额外的初始化代码。

当内存管理器丢弃对象时,会调用 Delete 方法。请注意,关闭文件等关键关闭操作不应放在 Delete 中,因为你无法始终确定何时会调用 Delete

抽象和最终

[编辑 | 编辑源代码]

用户定义类型和方法也可以通过在相应的声明中添加 AbstractFinal 来声明为抽象最终

Type AbstractType Abstract
Method AbstractMethod() Abstract
End Type

Type FinalType Final
Method FinalMethod() Final
Print "FinalType.FinalMethod"
End Method
End Type

声明用户定义类型为抽象意味着你无法使用 New 创建它的实例。但是,仍然可以扩展此类类型并创建这些派生类型的实例。声明方法为抽象意味着该方法没有实现,必须由派生类型实现。任何至少有一个抽象方法的用户定义类型本身都是抽象的。

声明用户定义类型为最终意味着它不能被扩展。声明方法为最终意味着派生类型不能覆盖该方法。最终用户定义类型的所有方法本身都是最终的。

抽象类型和方法主要用于创建“模板”类型和方法,这些类型和方法将实现细节留给派生类型。

最终类型和方法主要用于防止对类型的行为进行修改。

华夏公益教科书